]> granicus.if.org Git - icinga2/commitdiff
Implement Icinga1.x-style compat log file rotation
authorGunnar Beutner <gunnar.beutner@netways.de>
Wed, 20 Mar 2013 10:11:46 +0000 (11:11 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Wed, 20 Mar 2013 10:11:46 +0000 (11:11 +0100)
Fixes #3874

components/compat/compat-type.conf
components/compat/compatcomponent.cpp
components/compat/compatlog.cpp
components/compat/compatlog.h
lib/base/timer.cpp

index 83e56f4f570bad0f7eaa396fae53ffdf5c916a87..661b078ed1a0d51cc9fec11fb771b9e68f1e2d95 100644 (file)
@@ -24,8 +24,10 @@ type CompatComponent {
 }
 
 type CompatLog {
+       %validator "ValidateRotationMethod",
+
        %attribute string "path_prefix",
-       %attribute number "rotation_interval"
+       %attribute string "rotation_method"
 }
 
 type CheckResultReader {
index 7aa3ee7c6ce630055abf9068b8a5c932d349fe6c..9df83df2f5c96a1099da02bc621cbecd21d88d6b 100644 (file)
@@ -436,6 +436,9 @@ void CompatComponent::DumpServiceObject(std::ostream& fp, const Service::Ptr& se
                        notification_interval = notification->GetNotificationInterval();
        }
 
+       if (notification_interval == -1)
+               notification_interval = 60;
+
        {
                ObjectLock olock(service);
 
@@ -451,7 +454,7 @@ void CompatComponent::DumpServiceObject(std::ostream& fp, const Service::Ptr& se
                   << "\t" << "passive_checks_enabled" << "\t" << (service->GetEnablePassiveChecks() ? 1 : 0) << "\n"
                   << "\t" << "notifications_enabled" << "\t" << (service->GetEnableNotifications() ? 1 : 0) << "\n"
                   << "\t" << "notification_options" << "\t" << "u,w,c,r" << "\n"
-                  << "\t" << "notification_interval" << "\t" << notification_interval << "\n"
+                  << "\t" << "notification_interval" << "\t" << notification_interval / 60.0 << "\n"
                   << "\t" << "}" << "\n"
                   << "\n";
        }
index 01081626da203b7f83494c36f99ecf5842793975..c7bde6f6789e96e9d583ba99b2036a153ab18f20 100644 (file)
 #include "icinga/checkresultmessage.h"
 #include "icinga/service.h"
 #include "icinga/macroprocessor.h"
+#include "config/configcompilercontext.h"
 #include "base/dynamictype.h"
 #include "base/objectlock.h"
 #include "base/logger_fwd.h"
+#include "base/exception.h"
 #include "base/convert.h"
 #include "base/application.h"
 #include <boost/smart_ptr/make_shared.hpp>
 using namespace icinga;
 
 REGISTER_TYPE(CompatLog);
+REGISTER_SCRIPTFUNCTION(ValidateRotationMethod, &CompatLog::ValidateRotationMethod);
 
-CompatLog::CompatLog(const Dictionary::Ptr& properties)
-       : DynamicObject(properties)
+CompatLog::CompatLog(const Dictionary::Ptr& serializedUpdate)
+       : DynamicObject(serializedUpdate), m_LastRotation(0)
 {
        RegisterAttribute("log_dir", Attribute_Config, &m_LogDir);
-       RegisterAttribute("rotation_interval", Attribute_Config, &m_RotationInterval);
-}
-
-CompatLog::~CompatLog(void)
-{
+       RegisterAttribute("rotation_method", Attribute_Config, &m_RotationMethod);
 }
 
 /**
@@ -51,9 +50,8 @@ void CompatLog::OnAttributeChanged(const String& name)
 {
        ASSERT(!OwnsLock());
 
-       if (name == "rotation_interval") {
-               m_RotationTimer->SetInterval(GetRotationInterval());
-       }
+       if (name == "rotation_method")
+               ScheduleNextRotation();
 }
 
 /**
@@ -67,10 +65,10 @@ void CompatLog::Start(void)
 
        m_RotationTimer = boost::make_shared<Timer>();
        m_RotationTimer->OnTimerExpired.connect(boost::bind(&CompatLog::RotationTimerHandler, this));
-       m_RotationTimer->SetInterval(GetRotationInterval());
        m_RotationTimer->Start();
 
-       RotateFile();
+       ReopenFile(false);
+       ScheduleNextRotation();
 }
 
 /**
@@ -91,18 +89,18 @@ String CompatLog::GetLogDir(void) const
        if (!m_LogDir.IsEmpty())
                return m_LogDir;
        else
-               return Application::GetLocalStateDir() + "/log/icinga2/compat/";
+               return Application::GetLocalStateDir() + "/log/icinga2/compat";
 }
 
 /**
  * @threadsafety Always.
  */
-double CompatLog::GetRotationInterval(void) const
+String CompatLog::GetRotationMethod(void) const
 {
-       if (!m_RotationInterval.IsEmpty())
-               return m_RotationInterval;
+       if (!m_RotationMethod.IsEmpty())
+               return m_RotationMethod;
        else
-               return 3600;
+               return "HOURLY";
 }
 
 /**
@@ -196,20 +194,24 @@ void CompatLog::Flush(void)
 /**
  * @threadsafety Always.
  */
-void CompatLog::RotateFile(void)
+void CompatLog::ReopenFile(bool rotate)
 {
        ObjectLock olock(this);
 
        String tempFile = GetLogDir() + "/icinga.log";
 
-       if (m_OutputFile.good()) {
+       if (m_OutputFile) {
                m_OutputFile.close();
 
-               String finalFile = GetLogDir() + "/archives/icinga-" + Convert::ToString((long)Utility::GetTime()) + ".log";
-               (void) rename(tempFile.CStr(), finalFile.CStr());
+               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());
+               }
        }
 
-       m_OutputFile.open(tempFile.CStr());
+       m_OutputFile.open(tempFile.CStr(), std::ofstream::app);
 
        if (!m_OutputFile.good()) {
                Log(LogWarning, "icinga", "Could not open compat log file '" + tempFile + "' for writing. Log output will be lost.");
@@ -217,6 +219,7 @@ void CompatLog::RotateFile(void)
                return;
        }
 
+       WriteLine("LOG ROTATION: " + GetRotationMethod());
        WriteLine("LOG VERSION: 2.0");
 
        BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Host")) {
@@ -265,10 +268,89 @@ void CompatLog::RotateFile(void)
        Flush();
 }
 
+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);
+}
+
 /**
  * @threadsafety Always.
  */
 void CompatLog::RotationTimerHandler(void)
 {
-       RotateFile();
+       try {
+               ReopenFile(true);
+       } catch (...) {
+               ScheduleNextRotation();
+
+               throw;
+       }
+
+       ScheduleNextRotation();
+}
+
+void CompatLog::ValidateRotationMethod(const ScriptTask::Ptr& task, const std::vector<Value>& arguments)
+{
+       if (arguments.size() < 1)
+               BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Location must be specified."));
+
+       if (arguments.size() < 2)
+               BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Attribute dictionary must be specified."));
+
+       String location = arguments[0];
+       Dictionary::Ptr attrs = arguments[1];
+
+       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") {
+               ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
+                   location + ": Rotation method '" + rotation_method + "' is invalid.");
+       }
+
+       task->FinishResult(Empty);
 }
index 5ac710484a51d642b83d50bc2da4e56912b77005..e9b94cc13f5c830f67b0ff5ce81e566a8cda3852 100644 (file)
@@ -39,13 +39,14 @@ public:
        typedef shared_ptr<CompatLog> Ptr;
        typedef weak_ptr<CompatLog> WeakPtr;
 
-       CompatLog(const Dictionary::Ptr& properties);
-       ~CompatLog(void);
+       CompatLog(const Dictionary::Ptr& serializedUpdate);
 
        static CompatLog::Ptr GetByName(const String& name);
 
        String GetLogDir(void) const;
-       double GetRotationInterval(void) const;
+       String GetRotationMethod(void) const;
+
+       static void ValidateRotationMethod(const ScriptTask::Ptr& task, const std::vector<Value>& arguments);
 
 protected:
        virtual void OnAttributeChanged(const String& name);
@@ -53,7 +54,9 @@ protected:
 
 private:
        Attribute<String> m_LogDir;
-       Attribute<double> m_RotationInterval;
+       Attribute<String> m_RotationMethod;
+
+       double m_LastRotation;
 
        void WriteLine(const String& line);
        void Flush(void);
@@ -63,9 +66,10 @@ private:
 
        Timer::Ptr m_RotationTimer;
        void RotationTimerHandler(void);
+       void ScheduleNextRotation(void);
 
        std::ofstream m_OutputFile;
-       void RotateFile(void);
+       void ReopenFile(bool rotate);
 };
 
 }
index a87a0d1edd7272690bde5e5bc97b0da729274f95..7507229357ef4851c499a2163211fe1136139a09 100644 (file)
@@ -204,8 +204,13 @@ void Timer::Reschedule(double next)
 
        boost::mutex::scoped_lock lock(l_Mutex);
 
-       if (next < 0)
+       if (next < 0) {
+               /* Don't schedule the next call if this is not a periodic timer. */
+               if (m_Interval <= 0)
+                       return;
+
                next = Utility::GetTime() + m_Interval;
+       }
 
        m_Next = next;