]> granicus.if.org Git - icinga2/blob - components/compat/compatlogger.cpp
Remove the HostUnreachable state.
[icinga2] / components / compat / compatlogger.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
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.                     *
9  *                                                                            *
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.                               *
14  *                                                                            *
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  ******************************************************************************/
19
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>
40
41 using namespace icinga;
42
43 REGISTER_TYPE(CompatLogger);
44 REGISTER_SCRIPTFUNCTION(ValidateRotationMethod, &CompatLogger::ValidateRotationMethod);
45
46 REGISTER_STATSFUNCTION(CompatLoggerStats, &CompatLogger::StatsFunc);
47
48 Value CompatLogger::StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata)
49 {
50         Dictionary::Ptr nodes = make_shared<Dictionary>();
51
52         BOOST_FOREACH(const CompatLogger::Ptr& compat_logger, DynamicType::GetObjects<CompatLogger>()) {
53                 nodes->Set(compat_logger->GetName(), 1); //add more stats
54         }
55
56         status->Set("compatlogger", nodes);
57
58         return 0;
59 }
60
61 /**
62  * @threadsafety Always.
63  */
64 void CompatLogger::Start(void)
65 {
66         DynamicObject::Start();
67
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));
75
76         m_RotationTimer = make_shared<Timer>();
77         m_RotationTimer->OnTimerExpired.connect(boost::bind(&CompatLogger::RotationTimerHandler, this));
78         m_RotationTimer->Start();
79
80         ReopenFile(false);
81         ScheduleNextRotation();
82 }
83
84 /**
85  * @threadsafety Always.
86  */
87 void CompatLogger::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
88 {
89         Host::Ptr host;
90         Service::Ptr service;
91         tie(host, service) = GetHostService(checkable);
92
93         Dictionary::Ptr vars_after = cr->GetVarsAfter();
94
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");
100
101         Dictionary::Ptr vars_before = cr->GetVarsBefore();
102
103         if (vars_before) {
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");
108
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. */
112         }
113
114         String output;
115         if (cr)
116                 output = CompatUtility::GetCheckResultOutput(cr);
117
118         std::ostringstream msgbuf;
119
120         if (service) {
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 << ";"
127                        << output << ""
128                        << "";
129         } else {
130                 String state = Host::StateToString(Host::CalculateState(static_cast<ServiceState>(state_after)));
131
132                 if (!reachable_after)
133                         state = "UNREACHABLE";
134
135                 msgbuf << "HOST ALERT: "
136                        << host->GetName() << ";"
137                        << state << ";"
138                        << Host::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
139                        << attempt_after << ";"
140                        << output << ""
141                        << "";
142
143         }
144
145         {
146                 ObjectLock olock(this);
147                 WriteLine(msgbuf.str());
148                 Flush();
149         }
150 }
151
152 /**
153  * @threadsafety Always.
154  */
155 void CompatLogger::TriggerDowntimeHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
156 {
157         Host::Ptr host;
158         Service::Ptr service;
159         tie(host, service) = GetHostService(checkable);
160
161         if (!downtime)
162                 return;
163
164         std::ostringstream msgbuf;
165
166         if (service) {
167                 msgbuf << "SERVICE DOWNTIME ALERT: "
168                         << host->GetName() << ";"
169                         << service->GetShortName() << ";"
170                         << "STARTED" << "; "
171                         << "Checkable has entered a period of scheduled downtime."
172                         << "";
173         } else {
174                 msgbuf << "HOST DOWNTIME ALERT: "
175                         << host->GetName() << ";"
176                         << "STARTED" << "; "
177                         << "Checkable has entered a period of scheduled downtime."
178                         << "";
179         }
180
181         {
182                 ObjectLock oLock(this);
183                 WriteLine(msgbuf.str());
184                 Flush();
185         }
186 }
187
188 /**
189  * @threadsafety Always.
190  */
191 void CompatLogger::RemoveDowntimeHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
192 {
193         Host::Ptr host;
194         Service::Ptr service;
195         tie(host, service) = GetHostService(checkable);
196
197         if (!downtime)
198                 return;
199
200         String downtime_output;
201         String downtime_state_str;
202
203         if (downtime->GetWasCancelled()) {
204                 downtime_output = "Scheduled downtime for service has been cancelled.";
205                 downtime_state_str = "CANCELLED";
206         } else {
207                 downtime_output = "Checkable has exited from a period of scheduled downtime.";
208                 downtime_state_str = "STOPPED";
209         }
210
211         std::ostringstream msgbuf;
212
213         if (service) {
214                 msgbuf << "SERVICE DOWNTIME ALERT: "
215                         << host->GetName() << ";"
216                         << service->GetShortName() << ";"
217                         << downtime_state_str << "; "
218                         << downtime_output
219                         << "";
220         } else {
221                 msgbuf << "HOST DOWNTIME ALERT: "
222                         << host->GetName() << ";"
223                         << downtime_state_str << "; "
224                         << downtime_output
225                         << "";
226         }
227
228         {
229                 ObjectLock oLock(this);
230                 WriteLine(msgbuf.str());
231                 Flush();
232         }
233 }
234
235 /**
236  * @threadsafety Always.
237  */
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)
241 {
242         Host::Ptr host;
243         Service::Ptr service;
244         tie(host, service) = GetHostService(checkable);
245
246         String notification_type_str = Notification::NotificationTypeToString(notification_type);
247
248         /* override problem notifications with their current state string */
249         if (notification_type == NotificationProblem) {
250                 if (service)
251                         notification_type_str = Service::StateToString(service->GetState());
252                 else
253                         notification_type_str = host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE";
254         }
255
256         String author_comment = "";
257         if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
258                 author_comment = author + ";" + comment_text;
259         }
260
261         if (!cr)
262                 return;
263
264         String output;
265         if (cr)
266                 output = CompatUtility::GetCheckResultOutput(cr);
267
268         std::ostringstream msgbuf;
269
270         if (service) {
271                 msgbuf << "SERVICE NOTIFICATION: "
272                         << user->GetName() << ";"
273                         << host->GetName() << ";"
274                         << service->GetShortName() << ";"
275                         << notification_type_str << ";"
276                         << command_name << ";"
277                         << output << ";"
278                         << author_comment
279                         << "";
280         } else {
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 << ";"
287                         << output << ";"
288                         << author_comment
289                         << "";
290         }
291
292         {
293                 ObjectLock oLock(this);
294                 WriteLine(msgbuf.str());
295                 Flush();
296         }
297 }
298
299 /**
300  * @threadsafety Always.
301  */
302 void CompatLogger::FlappingHandler(const Checkable::Ptr& checkable, FlappingState flapping_state)
303 {
304         Host::Ptr host;
305         Service::Ptr service;
306         tie(host, service) = GetHostService(checkable);
307
308         String flapping_state_str;
309         String flapping_output;
310
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";
315                         break;
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";
319                         break;
320                 case FlappingDisabled:
321                         flapping_output = "Flap detection has been disabled";
322                         flapping_state_str = "DISABLED";
323                         break;
324                 default:
325                         Log(LogCritical, "compat", "Unknown flapping state: " + Convert::ToString(flapping_state));
326                         return;
327         }
328
329         std::ostringstream msgbuf;
330
331         if (service) {
332                 msgbuf << "SERVICE FLAPPING ALERT: "
333                         << host->GetName() << ";"
334                         << service->GetShortName() << ";"
335                         << flapping_state_str << "; "
336                         << flapping_output
337                         << "";
338         } else {
339                 msgbuf << "HOST FLAPPING ALERT: "
340                         << host->GetName() << ";"
341                         << flapping_state_str << "; "
342                         << flapping_output
343                         << "";
344         }
345
346         {
347                 ObjectLock oLock(this);
348                 WriteLine(msgbuf.str());
349                 Flush();
350         }
351 }
352
353 void CompatLogger::ExternalCommandHandler(const String& command, const std::vector<String>& arguments)
354 {
355         std::ostringstream msgbuf;
356         msgbuf << "EXTERNAL COMMAND: "
357                 << command << ";"
358                 << boost::algorithm::join(arguments, ";")
359                 << "";
360
361         {
362                 ObjectLock oLock(this);
363                 WriteLine(msgbuf.str());
364         }
365 }
366
367 void CompatLogger::EventCommandHandler(const Checkable::Ptr& checkable)
368 {
369         Host::Ptr host;
370         Service::Ptr service;
371         tie(host, service) = GetHostService(checkable);
372
373         EventCommand::Ptr event_command = checkable->GetEventCommand();
374         String event_command_name = event_command->GetName();
375         long current_attempt = checkable->GetCheckAttempt();
376
377         std::ostringstream msgbuf;
378
379         if (service) {
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;
387         } else {
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;
394         }
395
396         {
397                 ObjectLock oLock(this);
398                 WriteLine(msgbuf.str());
399                 Flush();
400         }
401 }
402
403 void CompatLogger::WriteLine(const String& line)
404 {
405         ASSERT(OwnsLock());
406
407         if (!m_OutputFile.good())
408                 return;
409
410         m_OutputFile << "[" << (long)Utility::GetTime() << "] " << line << "\n";
411 }
412
413 void CompatLogger::Flush(void)
414 {
415         ASSERT(OwnsLock());
416
417         if (!m_OutputFile.good())
418                 return;
419
420         m_OutputFile << std::flush;
421 }
422
423 /**
424  * @threadsafety Always.
425  */
426 void CompatLogger::ReopenFile(bool rotate)
427 {
428         ObjectLock olock(this);
429
430         String tempFile = GetLogDir() + "/icinga.log";
431
432         if (m_OutputFile) {
433                 m_OutputFile.close();
434
435                 if (rotate) {
436                         String archiveFile = GetLogDir() + "/archives/icinga-" + Utility::FormatDateTime("%m-%d-%Y-%H", Utility::GetTime()) + ".log";
437
438                         Log(LogInformation, "compat", "Rotating compat log file '" + tempFile + "' -> '" + archiveFile + "'");
439                         (void) rename(tempFile.CStr(), archiveFile.CStr());
440                 }
441         }
442
443         m_OutputFile.open(tempFile.CStr(), std::ofstream::app);
444
445         if (!m_OutputFile.good()) {
446                 Log(LogWarning, "icinga", "Could not open compat log file '" + tempFile + "' for writing. Log output will be lost.");
447
448                 return;
449         }
450
451         WriteLine("LOG ROTATION: " + GetRotationMethod());
452         WriteLine("LOG VERSION: 2.0");
453
454         BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
455                 String output;
456                 CheckResult::Ptr cr = host->GetLastCheckResult();
457
458                 if (cr)
459                         output = CompatUtility::GetCheckResultOutput(cr);
460
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() << ";"
467                        << output << "";
468
469                 WriteLine(msgbuf.str());
470         }
471
472         BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjects<Service>()) {
473                 Host::Ptr host = service->GetHost();
474
475                 String output;
476                 CheckResult::Ptr cr = service->GetLastCheckResult();
477
478                 if (cr)
479                         output = CompatUtility::GetCheckResultOutput(cr);
480
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() << ";"
488                        << output << "";
489
490                 WriteLine(msgbuf.str());
491         }
492
493         Flush();
494 }
495
496 void CompatLogger::ScheduleNextRotation(void)
497 {
498         time_t now = (time_t)Utility::GetTime();
499         String method = GetRotationMethod();
500
501         tm tmthen;
502
503 #ifdef _MSC_VER
504         tm *temp = localtime(&now);
505
506         if (temp == NULL) {
507                 BOOST_THROW_EXCEPTION(posix_error()
508                     << boost::errinfo_api_function("localtime")
509                     << boost::errinfo_errno(errno));
510         }
511
512         tmthen = *temp;
513 #else /* _MSC_VER */
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));
518         }
519 #endif /* _MSC_VER */
520
521         tmthen.tm_min = 0;
522         tmthen.tm_sec = 0;
523
524         if (method == "HOURLY") {
525                 tmthen.tm_hour++;
526         } else if (method == "DAILY") {
527                 tmthen.tm_mday++;
528                 tmthen.tm_hour = 0;
529         } else if (method == "WEEKLY") {
530                 tmthen.tm_mday += 7 - tmthen.tm_wday;
531                 tmthen.tm_hour = 0;
532         } else if (method == "MONTHLY") {
533                 tmthen.tm_mon++;
534                 tmthen.tm_mday = 1;
535                 tmthen.tm_hour = 0;
536         }
537
538         time_t ts = mktime(&tmthen);
539
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);
543 }
544
545 /**
546  * @threadsafety Always.
547  */
548 void CompatLogger::RotationTimerHandler(void)
549 {
550         try {
551                 ReopenFile(true);
552         } catch (...) {
553                 ScheduleNextRotation();
554
555                 throw;
556         }
557
558         ScheduleNextRotation();
559 }
560
561 void CompatLogger::ValidateRotationMethod(const String& location, const Dictionary::Ptr& attrs)
562 {
563         Value rotation_method = attrs->Get("rotation_method");
564
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.");
569         }
570 }
571