[icinga-checkins] icinga.org: icinga2/master: Implement Icinga1. x-style compat log file rotation

git at icinga.org git at icinga.org
Wed Mar 20 11:12:00 CET 2013


Module: icinga2
Branch: master
Commit: 1f570ea9f7313d11ea7e1364bc14c7dabc6f73b8
URL:    https://git.icinga.org/?p=icinga2.git;a=commit;h=1f570ea9f7313d11ea7e1364bc14c7dabc6f73b8

Author: Gunnar Beutner <gunnar.beutner at netways.de>
Date:   Wed Mar 20 11:11:46 2013 +0100

Implement Icinga1.x-style compat log file rotation

Fixes #3874

---

 components/compat/compat-type.conf    |    4 +-
 components/compat/compatcomponent.cpp |    5 +-
 components/compat/compatlog.cpp       |  128 +++++++++++++++++++++++++++------
 components/compat/compatlog.h         |   14 +++--
 lib/base/timer.cpp                    |    7 ++-
 5 files changed, 127 insertions(+), 31 deletions(-)

diff --git a/components/compat/compat-type.conf b/components/compat/compat-type.conf
index 83e56f4..661b078 100644
--- a/components/compat/compat-type.conf
+++ b/components/compat/compat-type.conf
@@ -24,8 +24,10 @@ type CompatComponent {
 }
 
 type CompatLog {
+	%validator "ValidateRotationMethod",
+
 	%attribute string "path_prefix",
-	%attribute number "rotation_interval"
+	%attribute string "rotation_method"
 }
 
 type CheckResultReader {
diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp
index 7aa3ee7..9df83df 100644
--- a/components/compat/compatcomponent.cpp
+++ b/components/compat/compatcomponent.cpp
@@ -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";
 	}
diff --git a/components/compat/compatlog.cpp b/components/compat/compatlog.cpp
index 0108162..c7bde6f 100644
--- a/components/compat/compatlog.cpp
+++ b/components/compat/compatlog.cpp
@@ -21,9 +21,11 @@
 #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>
@@ -32,16 +34,13 @@
 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);
 }
diff --git a/components/compat/compatlog.h b/components/compat/compatlog.h
index 5ac7104..e9b94cc 100644
--- a/components/compat/compatlog.h
+++ b/components/compat/compatlog.h
@@ -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);
 };
 
 }
diff --git a/lib/base/timer.cpp b/lib/base/timer.cpp
index a87a0d1..7507229 100644
--- a/lib/base/timer.cpp
+++ b/lib/base/timer.cpp
@@ -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;
 





More information about the icinga-checkins mailing list