1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
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.hpp"
21 #include "compat/compatlogger-ti.cpp"
22 #include "icinga/service.hpp"
23 #include "icinga/checkcommand.hpp"
24 #include "icinga/eventcommand.hpp"
25 #include "icinga/notification.hpp"
26 #include "icinga/macroprocessor.hpp"
27 #include "icinga/externalcommandprocessor.hpp"
28 #include "icinga/compatutility.hpp"
29 #include "base/configtype.hpp"
30 #include "base/objectlock.hpp"
31 #include "base/logger.hpp"
32 #include "base/exception.hpp"
33 #include "base/convert.hpp"
34 #include "base/application.hpp"
35 #include "base/utility.hpp"
36 #include "base/statsfunction.hpp"
37 #include <boost/algorithm/string.hpp>
39 using namespace icinga;
41 REGISTER_TYPE(CompatLogger);
43 REGISTER_STATSFUNCTION(CompatLogger, &CompatLogger::StatsFunc);
45 void CompatLogger::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
49 for (const CompatLogger::Ptr& compat_logger : ConfigType::GetObjectsByType<CompatLogger>()) {
50 nodes.emplace_back(compat_logger->GetName(), 1); // add more stats
53 status->Set("compatlogger", new Dictionary(std::move(nodes)));
57 * @threadsafety Always.
59 void CompatLogger::Start(bool runtimeCreated)
61 ObjectImpl<CompatLogger>::Start(runtimeCreated);
63 Log(LogInformation, "CompatLogger")
64 << "'" << GetName() << "' started.";
66 Log(LogWarning, "CompatLogger")
67 << "The CompatLogger feature is DEPRECATED and will be removed in Icinga v2.11.";
69 Checkable::OnNewCheckResult.connect(std::bind(&CompatLogger::CheckResultHandler, this, _1, _2));
70 Checkable::OnNotificationSentToUser.connect(std::bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8));
71 Downtime::OnDowntimeTriggered.connect(std::bind(&CompatLogger::TriggerDowntimeHandler, this, _1));
72 Downtime::OnDowntimeRemoved.connect(std::bind(&CompatLogger::RemoveDowntimeHandler, this, _1));
73 Checkable::OnEventCommandExecuted.connect(std::bind(&CompatLogger::EventCommandHandler, this, _1));
75 Checkable::OnFlappingChanged.connect(std::bind(&CompatLogger::FlappingChangedHandler, this, _1));
76 Checkable::OnEnableFlappingChanged.connect(std::bind(&CompatLogger::EnableFlappingChangedHandler, this, _1));
78 ExternalCommandProcessor::OnNewExternalCommand.connect(std::bind(&CompatLogger::ExternalCommandHandler, this, _2, _3));
80 m_RotationTimer = new Timer();
81 m_RotationTimer->OnTimerExpired.connect(std::bind(&CompatLogger::RotationTimerHandler, this));
82 m_RotationTimer->Start();
85 ScheduleNextRotation();
89 * @threadsafety Always.
91 void CompatLogger::Stop(bool runtimeRemoved)
93 Log(LogInformation, "CompatLogger")
94 << "'" << GetName() << "' stopped.";
96 ObjectImpl<CompatLogger>::Stop(runtimeRemoved);
100 * @threadsafety Always.
102 void CompatLogger::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
105 Service::Ptr service;
106 tie(host, service) = GetHostService(checkable);
108 Dictionary::Ptr vars_after = cr->GetVarsAfter();
110 long state_after = vars_after->Get("state");
111 long stateType_after = vars_after->Get("state_type");
112 long attempt_after = vars_after->Get("attempt");
113 bool reachable_after = vars_after->Get("reachable");
115 Dictionary::Ptr vars_before = cr->GetVarsBefore();
118 long state_before = vars_before->Get("state");
119 long stateType_before = vars_before->Get("state_type");
120 long attempt_before = vars_before->Get("attempt");
121 bool reachable_before = vars_before->Get("reachable");
123 if (state_before == state_after && stateType_before == stateType_after &&
124 attempt_before == attempt_after && reachable_before == reachable_after)
125 return; /* Nothing changed, ignore this checkresult. */
130 output = CompatUtility::GetCheckResultOutput(cr);
132 std::ostringstream msgbuf;
135 msgbuf << "SERVICE ALERT: "
136 << host->GetName() << ";"
137 << service->GetShortName() << ";"
138 << Service::StateToString(service->GetState()) << ";"
139 << Service::StateTypeToString(service->GetStateType()) << ";"
140 << attempt_after << ";"
144 String state = Host::StateToString(Host::CalculateState(static_cast<ServiceState>(state_after)));
146 msgbuf << "HOST ALERT: "
147 << host->GetName() << ";"
148 << GetHostStateString(host) << ";"
149 << Host::StateTypeToString(host->GetStateType()) << ";"
150 << attempt_after << ";"
157 ObjectLock olock(this);
158 WriteLine(msgbuf.str());
164 * @threadsafety Always.
166 void CompatLogger::TriggerDowntimeHandler(const Downtime::Ptr& downtime)
169 Service::Ptr service;
170 tie(host, service) = GetHostService(downtime->GetCheckable());
175 std::ostringstream msgbuf;
178 msgbuf << "SERVICE DOWNTIME ALERT: "
179 << host->GetName() << ";"
180 << service->GetShortName() << ";"
182 << "Checkable has entered a period of scheduled downtime."
185 msgbuf << "HOST DOWNTIME ALERT: "
186 << host->GetName() << ";"
188 << "Checkable has entered a period of scheduled downtime."
193 ObjectLock oLock(this);
194 WriteLine(msgbuf.str());
200 * @threadsafety Always.
202 void CompatLogger::RemoveDowntimeHandler(const Downtime::Ptr& downtime)
205 Service::Ptr service;
206 tie(host, service) = GetHostService(downtime->GetCheckable());
211 String downtime_output;
212 String downtime_state_str;
214 if (downtime->GetWasCancelled()) {
215 downtime_output = "Scheduled downtime for service has been cancelled.";
216 downtime_state_str = "CANCELLED";
218 downtime_output = "Checkable has exited from a period of scheduled downtime.";
219 downtime_state_str = "STOPPED";
222 std::ostringstream msgbuf;
225 msgbuf << "SERVICE DOWNTIME ALERT: "
226 << host->GetName() << ";"
227 << service->GetShortName() << ";"
228 << downtime_state_str << "; "
232 msgbuf << "HOST DOWNTIME ALERT: "
233 << host->GetName() << ";"
234 << downtime_state_str << "; "
240 ObjectLock oLock(this);
241 WriteLine(msgbuf.str());
247 * @threadsafety Always.
249 void CompatLogger::NotificationSentHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable,
250 const User::Ptr& user, NotificationType notification_type, CheckResult::Ptr const& cr,
251 const String& author, const String& comment_text, const String& command_name)
254 Service::Ptr service;
255 tie(host, service) = GetHostService(checkable);
257 String notification_type_str = Notification::NotificationTypeToString(notification_type);
259 /* override problem notifications with their current state string */
260 if (notification_type == NotificationProblem) {
262 notification_type_str = Service::StateToString(service->GetState());
264 notification_type_str = GetHostStateString(host);
267 String author_comment = "";
268 if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
269 author_comment = author + ";" + comment_text;
277 output = CompatUtility::GetCheckResultOutput(cr);
279 std::ostringstream msgbuf;
282 msgbuf << "SERVICE NOTIFICATION: "
283 << user->GetName() << ";"
284 << host->GetName() << ";"
285 << service->GetShortName() << ";"
286 << notification_type_str << ";"
287 << command_name << ";"
292 msgbuf << "HOST NOTIFICATION: "
293 << user->GetName() << ";"
294 << host->GetName() << ";"
295 << notification_type_str << " "
296 << "(" << GetHostStateString(host) << ");"
297 << command_name << ";"
304 ObjectLock oLock(this);
305 WriteLine(msgbuf.str());
311 * @threadsafety Always.
313 void CompatLogger::FlappingChangedHandler(const Checkable::Ptr& checkable)
316 Service::Ptr service;
317 tie(host, service) = GetHostService(checkable);
319 String flapping_state_str;
320 String flapping_output;
322 if (checkable->IsFlapping()) {
323 flapping_output = "Checkable appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThresholdHigh()) + "% threshold)";
324 flapping_state_str = "STARTED";
326 flapping_output = "Checkable appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThresholdLow()) + "% threshold)";
327 flapping_state_str = "STOPPED";
330 std::ostringstream msgbuf;
333 msgbuf << "SERVICE FLAPPING ALERT: "
334 << host->GetName() << ";"
335 << service->GetShortName() << ";"
336 << flapping_state_str << "; "
340 msgbuf << "HOST FLAPPING ALERT: "
341 << host->GetName() << ";"
342 << flapping_state_str << "; "
348 ObjectLock oLock(this);
349 WriteLine(msgbuf.str());
354 void CompatLogger::EnableFlappingChangedHandler(const Checkable::Ptr& checkable)
357 Service::Ptr service;
358 tie(host, service) = GetHostService(checkable);
360 if (checkable->GetEnableFlapping())
363 String flapping_output = "Flap detection has been disabled";
364 String flapping_state_str = "DISABLED";
366 std::ostringstream msgbuf;
369 msgbuf << "SERVICE FLAPPING ALERT: "
370 << host->GetName() << ";"
371 << service->GetShortName() << ";"
372 << flapping_state_str << "; "
376 msgbuf << "HOST FLAPPING ALERT: "
377 << host->GetName() << ";"
378 << flapping_state_str << "; "
384 ObjectLock oLock(this);
385 WriteLine(msgbuf.str());
390 void CompatLogger::ExternalCommandHandler(const String& command, const std::vector<String>& arguments)
392 std::ostringstream msgbuf;
393 msgbuf << "EXTERNAL COMMAND: "
395 << boost::algorithm::join(arguments, ";")
399 ObjectLock oLock(this);
400 WriteLine(msgbuf.str());
405 void CompatLogger::EventCommandHandler(const Checkable::Ptr& checkable)
408 Service::Ptr service;
409 tie(host, service) = GetHostService(checkable);
411 EventCommand::Ptr event_command = checkable->GetEventCommand();
412 String event_command_name = event_command->GetName();
413 long current_attempt = checkable->GetCheckAttempt();
415 std::ostringstream msgbuf;
418 msgbuf << "SERVICE EVENT HANDLER: "
419 << host->GetName() << ";"
420 << service->GetShortName() << ";"
421 << Service::StateToString(service->GetState()) << ";"
422 << Service::StateTypeToString(service->GetStateType()) << ";"
423 << current_attempt << ";"
424 << event_command_name;
426 msgbuf << "HOST EVENT HANDLER: "
427 << host->GetName() << ";"
428 << GetHostStateString(host) << ";"
429 << Host::StateTypeToString(host->GetStateType()) << ";"
430 << current_attempt << ";"
431 << event_command_name;
435 ObjectLock oLock(this);
436 WriteLine(msgbuf.str());
441 String CompatLogger::GetHostStateString(const Host::Ptr& host)
443 if (host->GetState() != HostUp && !host->IsReachable())
444 return "UNREACHABLE"; /* hardcoded compat state */
446 return Host::StateToString(host->GetState());
449 void CompatLogger::WriteLine(const String& line)
453 if (!m_OutputFile.good())
456 m_OutputFile << "[" << (long)Utility::GetTime() << "] " << line << "\n";
459 void CompatLogger::Flush()
463 if (!m_OutputFile.good())
466 m_OutputFile << std::flush;
470 * @threadsafety Always.
472 void CompatLogger::ReopenFile(bool rotate)
474 ObjectLock olock(this);
476 String tempFile = GetLogDir() + "/icinga.log";
479 m_OutputFile.close();
482 String archiveFile = GetLogDir() + "/archives/icinga-" + Utility::FormatDateTime("%m-%d-%Y-%H", Utility::GetTime()) + ".log";
484 Log(LogNotice, "CompatLogger")
485 << "Rotating compat log file '" << tempFile << "' -> '" << archiveFile << "'";
487 (void) rename(tempFile.CStr(), archiveFile.CStr());
491 m_OutputFile.open(tempFile.CStr(), std::ofstream::app);
494 Log(LogWarning, "CompatLogger")
495 << "Could not open compat log file '" << tempFile << "' for writing. Log output will be lost.";
500 WriteLine("LOG ROTATION: " + GetRotationMethod());
501 WriteLine("LOG VERSION: 2.0");
503 for (const Host::Ptr& host : ConfigType::GetObjectsByType<Host>()) {
505 CheckResult::Ptr cr = host->GetLastCheckResult();
508 output = CompatUtility::GetCheckResultOutput(cr);
510 std::ostringstream msgbuf;
511 msgbuf << "CURRENT HOST STATE: "
512 << host->GetName() << ";"
513 << GetHostStateString(host) << ";"
514 << Host::StateTypeToString(host->GetStateType()) << ";"
515 << host->GetCheckAttempt() << ";"
518 WriteLine(msgbuf.str());
521 for (const Service::Ptr& service : ConfigType::GetObjectsByType<Service>()) {
522 Host::Ptr host = service->GetHost();
525 CheckResult::Ptr cr = service->GetLastCheckResult();
528 output = CompatUtility::GetCheckResultOutput(cr);
530 std::ostringstream msgbuf;
531 msgbuf << "CURRENT SERVICE STATE: "
532 << host->GetName() << ";"
533 << service->GetShortName() << ";"
534 << Service::StateToString(service->GetState()) << ";"
535 << Service::StateTypeToString(service->GetStateType()) << ";"
536 << service->GetCheckAttempt() << ";"
539 WriteLine(msgbuf.str());
545 void CompatLogger::ScheduleNextRotation()
547 auto now = (time_t)Utility::GetTime();
548 String method = GetRotationMethod();
553 tm *temp = localtime(&now);
556 BOOST_THROW_EXCEPTION(posix_error()
557 << boost::errinfo_api_function("localtime")
558 << boost::errinfo_errno(errno));
563 if (!localtime_r(&now, &tmthen)) {
564 BOOST_THROW_EXCEPTION(posix_error()
565 << boost::errinfo_api_function("localtime_r")
566 << boost::errinfo_errno(errno));
568 #endif /* _MSC_VER */
573 if (method == "HOURLY") {
575 } else if (method == "DAILY") {
578 } else if (method == "WEEKLY") {
579 tmthen.tm_mday += 7 - tmthen.tm_wday;
581 } else if (method == "MONTHLY") {
587 time_t ts = mktime(&tmthen);
589 Log(LogNotice, "CompatLogger")
590 << "Rescheduling rotation timer for compat log '"
591 << GetName() << "' to '" << Utility::FormatDateTime("%Y/%m/%d %H:%M:%S %z", ts) << "'";
593 m_RotationTimer->Reschedule(ts);
597 * @threadsafety Always.
599 void CompatLogger::RotationTimerHandler()
604 ScheduleNextRotation();
609 ScheduleNextRotation();
612 void CompatLogger::ValidateRotationMethod(const Lazy<String>& lvalue, const ValidationUtils& utils)
614 ObjectImpl<CompatLogger>::ValidateRotationMethod(lvalue, utils);
616 if (lvalue() != "HOURLY" && lvalue() != "DAILY" &&
617 lvalue() != "WEEKLY" && lvalue() != "MONTHLY" && lvalue() != "NONE") {
618 BOOST_THROW_EXCEPTION(ValidationError(this, { "rotation_method" }, "Rotation method '" + lvalue() + "' is invalid."));