From 6055cd194a8397d7c228ea75d8bc3e7f45682e0b Mon Sep 17 00:00:00 2001 From: Marius Sturm Date: Wed, 5 Nov 2014 22:00:44 +0100 Subject: [PATCH 1/7] Add GelfWriter refs #7619 --- lib/perfdata/CMakeLists.txt | 3 +- lib/perfdata/gelfwriter.cpp | 201 ++++++++++++++++++++++++++++++++++++ lib/perfdata/gelfwriter.hpp | 65 ++++++++++++ lib/perfdata/gelfwriter.ti | 41 ++++++++ 4 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 lib/perfdata/gelfwriter.cpp create mode 100644 lib/perfdata/gelfwriter.hpp create mode 100644 lib/perfdata/gelfwriter.ti diff --git a/lib/perfdata/CMakeLists.txt b/lib/perfdata/CMakeLists.txt index 16dda3518..6a57e7559 100644 --- a/lib/perfdata/CMakeLists.txt +++ b/lib/perfdata/CMakeLists.txt @@ -15,13 +15,14 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +mkclass_target(gelfwriter.ti gelfwriter.thpp) mkclass_target(graphitewriter.ti graphitewriter.thpp) mkclass_target(perfdatawriter.ti perfdatawriter.thpp) mkembedconfig_target(perfdata-type.conf perfdata-type.cpp) set(perfdata_SOURCES - graphitewriter.cpp graphitewriter.thpp perfdatawriter.cpp perfdatawriter.thpp perfdata-type.cpp + gelfwriter.cpp gelfwriter.thpp graphitewriter.cpp graphitewriter.thpp perfdatawriter.cpp perfdatawriter.thpp perfdata-type.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp new file mode 100644 index 000000000..29723c7d0 --- /dev/null +++ b/lib/perfdata/gelfwriter.cpp @@ -0,0 +1,201 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "perfdata/gelfwriter.hpp" +#include "icinga/service.hpp" +#include "icinga/notification.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/compatutility.hpp" +#include "icinga/perfdatavalue.hpp" +#include "base/tcpsocket.hpp" +#include "base/dynamictype.hpp" +#include "base/objectlock.hpp" +#include "base/logger.hpp" +#include "base/utility.hpp" +#include "base/stream.hpp" +#include "base/networkstream.hpp" +#include "base/json.hpp" + +using namespace icinga; + +REGISTER_TYPE(GelfWriter); + +void GelfWriter::Start(void) +{ + DynamicObject::Start(); + + m_ReconnectTimer = make_shared(); + m_ReconnectTimer->SetInterval(10); + m_ReconnectTimer->OnTimerExpired.connect(boost::bind(&GelfWriter::ReconnectTimerHandler, this)); + m_ReconnectTimer->Start(); + m_ReconnectTimer->Reschedule(0); + + // Send check results + Service::OnNewCheckResult.connect(boost::bind(&GelfWriter::CheckResultHandler, this, _1, _2)); + // Send notifications + Service::OnNotificationSentToUser.connect(boost::bind(&GelfWriter::NotificationToUserHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); + // Send state change + Service::OnStateChange.connect(boost::bind(&GelfWriter::StateChangeHandler, this, _1, _2, _3)); +} + +void GelfWriter::ReconnectTimerHandler(void) +{ + if (m_Stream) + return; + + TcpSocket::Ptr socket = make_shared(); + + Log(LogNotice, "GelfWriter") + << "Reconnecting to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; + + try { + socket->Connect(GetHost(), GetPort()); + } catch (std::exception&) { + Log(LogCritical, "GelfWriter") + << "Can't connect to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; + return; + } + + m_Stream = make_shared(socket); +} + +void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) +{ + CONTEXT("GELF Processing check result for '" + checkable->GetName() + "'"); + + Dictionary::Ptr fields = make_shared(); + Service::Ptr service = dynamic_pointer_cast(checkable); + Host::Ptr host; + + if (service) { + host = service->GetHost(); + fields->Set("_service_name", service->GetShortName()); + fields->Set("_service_state", Service::StateToString(service->GetState())); + } else { + host = static_pointer_cast(checkable); + } + fields->Set("_hostname", host->GetName()); + fields->Set("short_message", cr->GetOutput()); + fields->Set("_type", "CHECK RESULT"); + + SendLogMessage(ComposeGelfMessage(fields, "icinga")); +} + +void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, + const User::Ptr& user, NotificationType notification_type, CheckResult::Ptr const& cr, + const String& author, const String& comment_text, const String& command_name) +{ + CONTEXT("GELF Processing notification to all users '" + checkable->GetName() + "'"); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + String notification_type_str = Notification::NotificationTypeToString(notification_type); + + String author_comment = ""; + if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) { + author_comment = author + ";" + comment_text; + } + + String output; + if (cr) + output = CompatUtility::GetCheckResultOutput(cr); + + Dictionary::Ptr fields = make_shared(); + if (service) { + host = service->GetHost(); + fields->Set("_type", "SERVICE NOTIFICATION"); + fields->Set("_service", service->GetShortName()); + fields->Set("short_message", output); + } else { + host = static_pointer_cast(checkable); + fields->Set("_type", "HOST NOTIFICATION"); + fields->Set("short_message", "(" << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ")"); + } + fields->Set("_hostname", host->GetName()); + fields->Set("_command", command_name); + fields->Set("_state", notification_type_str); + fields->Set("_comment", author_comment); + + SendLogMessage(ComposeGelfMessage(fields, "icinga")); +} + +void GelfWriter::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) +{ + CONTEXT("GELF Processing state change '" + checkable->GetName() + "'"); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr fields = make_shared(); + fields->Set("_state", service ? static_cast(service->GetState()) : static_cast(host->GetState())); + fields->Set("_type", "STATE CHANGE"); + fields->Set("_current_check_attempt", checkable->GetCheckAttempt()); + fields->Set("_max_check_attempts", checkable->GetMaxCheckAttempts()); + + if (service) { + fields->Set("_last_state", service->GetLastState()); + fields->Set("_last_hard_state", service->GetLastHardState()); + } else { + fields->Set("_last_state", host->GetLastState()); + fields->Set("_last_hard_state", host->GetLastHardState()); + } + + if (cr) { + fields->Set("short_message", CompatUtility::GetCheckResultOutput(cr)); + fields->Set("full_message", CompatUtility::GetCheckResultLongOutput(cr)); + fields->Set("_check_source", cr->GetCheckSource()); + } + + SendLogMessage(ComposeGelfMessage(fields, "icinga")); +} + +String GelfWriter::ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source) +{ + fields->Set("version", "1.1"); + fields->Set("host", source); + fields->Set("timestamp", Utility::GetTime()); + + return JsonEncode(fields); +} + +void GelfWriter::SendLogMessage(const String& gelf) +{ + std::ostringstream msgbuf; + msgbuf << gelf; + msgbuf << '\0'; + + String log = msgbuf.str(); + + ObjectLock olock(this); + + if (!m_Stream) + return; + + try { + m_Stream->Write(log.CStr(), log.GetLength()); + } catch (const std::exception& ex) { + Log(LogCritical, "GelfWriter") + << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; + + m_Stream.reset(); + } +} diff --git a/lib/perfdata/gelfwriter.hpp b/lib/perfdata/gelfwriter.hpp new file mode 100644 index 000000000..a15fc9a8e --- /dev/null +++ b/lib/perfdata/gelfwriter.hpp @@ -0,0 +1,65 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef GELFWRITER_H +#define GELFWRITER_H + +#include "perfdata/gelfwriter.thpp" +#include "icinga/service.hpp" +#include "base/dynamicobject.hpp" +#include "base/tcpsocket.hpp" +#include "base/timer.hpp" +#include + +namespace icinga +{ + +/** + * An Icinga gelf writer. + * + * @ingroup perfdata + */ +class GelfWriter : public ObjectImpl +{ +public: + DECLARE_OBJECT(GelfWriter); + DECLARE_OBJECTNAME(GelfWriter); + +protected: + virtual void Start(void); + +private: + Stream::Ptr m_Stream; + + Timer::Ptr m_ReconnectTimer; + + void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); + void NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, + const User::Ptr& user, NotificationType notification_type, CheckResult::Ptr const& cr, + const String& author, const String& comment_text, const String& command_name); + String ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source); + void StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type); + void SendLogMessage(const String& gelf); + + void ReconnectTimerHandler(void); +}; + +} + +#endif /* GELFWRITER_H */ diff --git a/lib/perfdata/gelfwriter.ti b/lib/perfdata/gelfwriter.ti new file mode 100644 index 000000000..5018f705e --- /dev/null +++ b/lib/perfdata/gelfwriter.ti @@ -0,0 +1,41 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/dynamicobject.hpp" + +namespace icinga +{ + +class GelfWriter : DynamicObject +{ + [config] String host { + default {{{ return "127.0.0.1"; }}} + }; + [config] String port { + default {{{ return "12201"; }}} + }; + [config] String host_name_template { + default {{{ return "icinga.$host.name$"; }}} + }; + [config] String service_name_template { + default {{{ return "icinga.$host.name$.$service.name$"; }}} + }; +}; + +} From a4ed44247ecd7023f9e353a5e75df20a4596287b Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 10 Nov 2014 20:05:57 +0100 Subject: [PATCH 2/7] Add 'gelf' feature config refs #7619 --- etc/CMakeLists.txt | 1 + etc/icinga2/features-available/gelf.conf | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 etc/icinga2/features-available/gelf.conf diff --git a/etc/CMakeLists.txt b/etc/CMakeLists.txt index 204e1e85e..08d781a75 100644 --- a/etc/CMakeLists.txt +++ b/etc/CMakeLists.txt @@ -41,6 +41,7 @@ install_if_not_exists(icinga2/features-available/checker.conf ${CMAKE_INSTALL_SY install_if_not_exists(icinga2/features-available/command.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) install_if_not_exists(icinga2/features-available/compatlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) install_if_not_exists(icinga2/features-available/debuglog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) +install_if_not_exists(icinga2/features-available/gelf.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) install_if_not_exists(icinga2/features-available/graphite.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) install_if_not_exists(icinga2/features-available/icingastatus.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) install_if_not_exists(icinga2/features-available/ido-mysql.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) diff --git a/etc/icinga2/features-available/gelf.conf b/etc/icinga2/features-available/gelf.conf new file mode 100644 index 000000000..1c8e6bd7e --- /dev/null +++ b/etc/icinga2/features-available/gelf.conf @@ -0,0 +1,11 @@ +/** + * The GelfWriter type writes event logs + * to a gelf tcp socket provided by graylog2. + */ + +library "perfdata" + +object GelfWriter "graylog2" { + //host = "127.0.0.1" + //port = 2003 +} From 721483e0b24220417605380edaf8ebc781f2e7ca Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 10 Nov 2014 20:09:54 +0100 Subject: [PATCH 3/7] Add a type validafor for GelfWriter refs #7619 --- lib/perfdata/gelfwriter.ti | 6 ------ lib/perfdata/perfdata-type.conf | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/perfdata/gelfwriter.ti b/lib/perfdata/gelfwriter.ti index 5018f705e..a3c49ab79 100644 --- a/lib/perfdata/gelfwriter.ti +++ b/lib/perfdata/gelfwriter.ti @@ -30,12 +30,6 @@ class GelfWriter : DynamicObject [config] String port { default {{{ return "12201"; }}} }; - [config] String host_name_template { - default {{{ return "icinga.$host.name$"; }}} - }; - [config] String service_name_template { - default {{{ return "icinga.$host.name$.$service.name$"; }}} - }; }; } diff --git a/lib/perfdata/perfdata-type.conf b/lib/perfdata/perfdata-type.conf index 011a881c1..d5bdff5b6 100644 --- a/lib/perfdata/perfdata-type.conf +++ b/lib/perfdata/perfdata-type.conf @@ -33,3 +33,9 @@ %attribute %string "host_name_template", %attribute %string "service_name_template" } + +%type GelfWriter { + %attribute %string "host", + %attribute %string "port" +} + From 3211756656ea4693abcb61e9712162668070773a Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 10 Nov 2014 20:21:22 +0100 Subject: [PATCH 4/7] Rebase against master changes, fix indent refs #7619 --- lib/perfdata/gelfwriter.cpp | 210 ++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp index 29723c7d0..4bcc06316 100644 --- a/lib/perfdata/gelfwriter.cpp +++ b/lib/perfdata/gelfwriter.cpp @@ -40,61 +40,61 @@ void GelfWriter::Start(void) { DynamicObject::Start(); - m_ReconnectTimer = make_shared(); - m_ReconnectTimer->SetInterval(10); - m_ReconnectTimer->OnTimerExpired.connect(boost::bind(&GelfWriter::ReconnectTimerHandler, this)); - m_ReconnectTimer->Start(); - m_ReconnectTimer->Reschedule(0); + m_ReconnectTimer = new Timer(); + m_ReconnectTimer->SetInterval(10); + m_ReconnectTimer->OnTimerExpired.connect(boost::bind(&GelfWriter::ReconnectTimerHandler, this)); + m_ReconnectTimer->Start(); + m_ReconnectTimer->Reschedule(0); - // Send check results + // Send check results Service::OnNewCheckResult.connect(boost::bind(&GelfWriter::CheckResultHandler, this, _1, _2)); - // Send notifications - Service::OnNotificationSentToUser.connect(boost::bind(&GelfWriter::NotificationToUserHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); - // Send state change - Service::OnStateChange.connect(boost::bind(&GelfWriter::StateChangeHandler, this, _1, _2, _3)); + // Send notifications + Service::OnNotificationSentToUser.connect(boost::bind(&GelfWriter::NotificationToUserHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); + // Send state change + Service::OnStateChange.connect(boost::bind(&GelfWriter::StateChangeHandler, this, _1, _2, _3)); } void GelfWriter::ReconnectTimerHandler(void) { - if (m_Stream) - return; + if (m_Stream) + return; - TcpSocket::Ptr socket = make_shared(); + TcpSocket::Ptr socket = new TcpSocket(); - Log(LogNotice, "GelfWriter") - << "Reconnecting to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; + Log(LogNotice, "GelfWriter") + << "Reconnecting to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; - try { - socket->Connect(GetHost(), GetPort()); - } catch (std::exception&) { - Log(LogCritical, "GelfWriter") - << "Can't connect to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; - return; - } + try { + socket->Connect(GetHost(), GetPort()); + } catch (std::exception&) { + Log(LogCritical, "GelfWriter") + << "Can't connect to GELF endpoint '" << GetHost() << "' port '" << GetPort() << "'."; + return; + } - m_Stream = make_shared(socket); + m_Stream = new NetworkStream(socket); } void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { CONTEXT("GELF Processing check result for '" + checkable->GetName() + "'"); - Dictionary::Ptr fields = make_shared(); - Service::Ptr service = dynamic_pointer_cast(checkable); - Host::Ptr host; + Dictionary::Ptr fields = new Dictionary(); + Service::Ptr service = dynamic_pointer_cast(checkable); + Host::Ptr host; - if (service) { - host = service->GetHost(); - fields->Set("_service_name", service->GetShortName()); - fields->Set("_service_state", Service::StateToString(service->GetState())); - } else { - host = static_pointer_cast(checkable); - } - fields->Set("_hostname", host->GetName()); - fields->Set("short_message", cr->GetOutput()); - fields->Set("_type", "CHECK RESULT"); + if (service) { + host = service->GetHost(); + fields->Set("_service_name", service->GetShortName()); + fields->Set("_service_state", Service::StateToString(service->GetState())); + } else { + host = static_pointer_cast(checkable); + } + fields->Set("_hostname", host->GetName()); + fields->Set("short_message", cr->GetOutput()); + fields->Set("_type", "CHECK RESULT"); - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + SendLogMessage(ComposeGelfMessage(fields, "icinga")); } void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, @@ -102,100 +102,100 @@ void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification const String& author, const String& comment_text, const String& command_name) { CONTEXT("GELF Processing notification to all users '" + checkable->GetName() + "'"); - - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); - String notification_type_str = Notification::NotificationTypeToString(notification_type); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); - String author_comment = ""; - if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) { - author_comment = author + ";" + comment_text; - } + String notification_type_str = Notification::NotificationTypeToString(notification_type); - String output; - if (cr) - output = CompatUtility::GetCheckResultOutput(cr); + String author_comment = ""; + if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) { + author_comment = author + ";" + comment_text; + } - Dictionary::Ptr fields = make_shared(); - if (service) { - host = service->GetHost(); - fields->Set("_type", "SERVICE NOTIFICATION"); - fields->Set("_service", service->GetShortName()); - fields->Set("short_message", output); - } else { - host = static_pointer_cast(checkable); - fields->Set("_type", "HOST NOTIFICATION"); - fields->Set("short_message", "(" << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ")"); - } - fields->Set("_hostname", host->GetName()); - fields->Set("_command", command_name); - fields->Set("_state", notification_type_str); - fields->Set("_comment", author_comment); + String output; + if (cr) + output = CompatUtility::GetCheckResultOutput(cr); - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + Dictionary::Ptr fields = new Dictionary(); + if (service) { + host = service->GetHost(); + fields->Set("_type", "SERVICE NOTIFICATION"); + fields->Set("_service", service->GetShortName()); + fields->Set("short_message", output); + } else { + host = static_pointer_cast(checkable); + fields->Set("_type", "HOST NOTIFICATION"); + fields->Set("short_message", "(" << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ")"); + } + fields->Set("_hostname", host->GetName()); + fields->Set("_command", command_name); + fields->Set("_state", notification_type_str); + fields->Set("_comment", author_comment); + + SendLogMessage(ComposeGelfMessage(fields, "icinga")); } void GelfWriter::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) { - CONTEXT("GELF Processing state change '" + checkable->GetName() + "'"); + CONTEXT("GELF Processing state change '" + checkable->GetName() + "'"); - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); - Dictionary::Ptr fields = make_shared(); - fields->Set("_state", service ? static_cast(service->GetState()) : static_cast(host->GetState())); - fields->Set("_type", "STATE CHANGE"); - fields->Set("_current_check_attempt", checkable->GetCheckAttempt()); - fields->Set("_max_check_attempts", checkable->GetMaxCheckAttempts()); + Dictionary::Ptr fields = new Dictionary(); + fields->Set("_state", service ? static_cast(service->GetState()) : static_cast(host->GetState())); + fields->Set("_type", "STATE CHANGE"); + fields->Set("_current_check_attempt", checkable->GetCheckAttempt()); + fields->Set("_max_check_attempts", checkable->GetMaxCheckAttempts()); - if (service) { - fields->Set("_last_state", service->GetLastState()); - fields->Set("_last_hard_state", service->GetLastHardState()); - } else { - fields->Set("_last_state", host->GetLastState()); - fields->Set("_last_hard_state", host->GetLastHardState()); - } + if (service) { + fields->Set("_last_state", service->GetLastState()); + fields->Set("_last_hard_state", service->GetLastHardState()); + } else { + fields->Set("_last_state", host->GetLastState()); + fields->Set("_last_hard_state", host->GetLastHardState()); + } - if (cr) { - fields->Set("short_message", CompatUtility::GetCheckResultOutput(cr)); - fields->Set("full_message", CompatUtility::GetCheckResultLongOutput(cr)); - fields->Set("_check_source", cr->GetCheckSource()); - } + if (cr) { + fields->Set("short_message", CompatUtility::GetCheckResultOutput(cr)); + fields->Set("full_message", CompatUtility::GetCheckResultLongOutput(cr)); + fields->Set("_check_source", cr->GetCheckSource()); + } - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + SendLogMessage(ComposeGelfMessage(fields, "icinga")); } String GelfWriter::ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source) { - fields->Set("version", "1.1"); - fields->Set("host", source); - fields->Set("timestamp", Utility::GetTime()); + fields->Set("version", "1.1"); + fields->Set("host", source); + fields->Set("timestamp", Utility::GetTime()); - return JsonEncode(fields); + return JsonEncode(fields); } void GelfWriter::SendLogMessage(const String& gelf) { - std::ostringstream msgbuf; - msgbuf << gelf; - msgbuf << '\0'; + std::ostringstream msgbuf; + msgbuf << gelf; + msgbuf << '\0'; - String log = msgbuf.str(); + String log = msgbuf.str(); - ObjectLock olock(this); + ObjectLock olock(this); - if (!m_Stream) - return; + if (!m_Stream) + return; - try { - m_Stream->Write(log.CStr(), log.GetLength()); - } catch (const std::exception& ex) { - Log(LogCritical, "GelfWriter") - << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; + try { + m_Stream->Write(log.CStr(), log.GetLength()); + } catch (const std::exception& ex) { + Log(LogCritical, "GelfWriter") + << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; - m_Stream.reset(); - } + m_Stream.reset(); + } } From f838fc4e6ce14c69ad11e5e763d1f94108dfdf45 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 10 Nov 2014 20:34:04 +0100 Subject: [PATCH 5/7] GelfWriter: Fix default example port refs #7619 --- etc/icinga2/features-available/gelf.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/icinga2/features-available/gelf.conf b/etc/icinga2/features-available/gelf.conf index 1c8e6bd7e..a9d180b51 100644 --- a/etc/icinga2/features-available/gelf.conf +++ b/etc/icinga2/features-available/gelf.conf @@ -7,5 +7,5 @@ library "perfdata" object GelfWriter "graylog2" { //host = "127.0.0.1" - //port = 2003 + //port = 12201 } From 5ac03849d209a74ae2c63cd5959609bc56b99eeb Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 11 Nov 2014 14:05:28 +0100 Subject: [PATCH 6/7] GelfWriter: Add 'source' configuration attribute refs #7619 --- lib/perfdata/gelfwriter.cpp | 9 ++++++--- lib/perfdata/gelfwriter.ti | 3 +++ lib/perfdata/perfdata-type.conf | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp index 4bcc06316..9e6bdb95f 100644 --- a/lib/perfdata/gelfwriter.cpp +++ b/lib/perfdata/gelfwriter.cpp @@ -79,6 +79,9 @@ void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const Check { CONTEXT("GELF Processing check result for '" + checkable->GetName() + "'"); + Log(LogDebug, "GelfWriter") + << "GELF Processing check result for '" << checkable->GetName() << "'"; + Dictionary::Ptr fields = new Dictionary(); Service::Ptr service = dynamic_pointer_cast(checkable); Host::Ptr host; @@ -94,7 +97,7 @@ void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const Check fields->Set("short_message", cr->GetOutput()); fields->Set("_type", "CHECK RESULT"); - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + SendLogMessage(ComposeGelfMessage(fields, GetSource())); } void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, @@ -134,7 +137,7 @@ void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification fields->Set("_state", notification_type_str); fields->Set("_comment", author_comment); - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + SendLogMessage(ComposeGelfMessage(fields, GetSource())); } void GelfWriter::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) @@ -165,7 +168,7 @@ void GelfWriter::StateChangeHandler(const Checkable::Ptr& checkable, const Check fields->Set("_check_source", cr->GetCheckSource()); } - SendLogMessage(ComposeGelfMessage(fields, "icinga")); + SendLogMessage(ComposeGelfMessage(fields, GetSource())); } String GelfWriter::ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source) diff --git a/lib/perfdata/gelfwriter.ti b/lib/perfdata/gelfwriter.ti index a3c49ab79..f5e4d20de 100644 --- a/lib/perfdata/gelfwriter.ti +++ b/lib/perfdata/gelfwriter.ti @@ -30,6 +30,9 @@ class GelfWriter : DynamicObject [config] String port { default {{{ return "12201"; }}} }; + [config] String source { + default {{{ return "icinga2"; }}} + }; }; } diff --git a/lib/perfdata/perfdata-type.conf b/lib/perfdata/perfdata-type.conf index d5bdff5b6..611c89591 100644 --- a/lib/perfdata/perfdata-type.conf +++ b/lib/perfdata/perfdata-type.conf @@ -36,6 +36,7 @@ %type GelfWriter { %attribute %string "host", - %attribute %string "port" + %attribute %string "port", + %attribute %string "source" } From 182042556263f30b34e6950b8db17990936ff8df Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 11 Nov 2014 14:12:06 +0100 Subject: [PATCH 7/7] GelfWriter: Add documentation refs #7619 --- doc/1-about.md | 8 ++++++++ doc/4-monitoring-basics.md | 25 +++++++++++++++++++++++- doc/7-configuring-icinga-2.md | 22 +++++++++++++++++++++ etc/icinga2/features-available/gelf.conf | 7 ++++--- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/doc/1-about.md b/doc/1-about.md index 6a6e93fa5..95ea33039 100644 --- a/doc/1-about.md +++ b/doc/1-about.md @@ -62,6 +62,8 @@ Icinga 2 is available as [Vagrant Demo VM](#vagrant). * schema files in `lib/db_ido_{mysql,pgsql}/schema` (source) * Table `programstatus`: New column `program_version` * Table `customvariables` and `customvariablestatus`: New column `is_json` (required for custom attribute array/dictionary support) +* New features + * [GelfWriter](#gelfwriter): Logging check results, state changes, notifications to GELF (graylog2, logstash) #7619 * New CLI commands #7245 * `icinga2 feature {enable,disable}` replaces `icinga2-{enable,disable}-feature` script #7250 * `icinga2 object list` replaces `icinga2-list-objects` script #7251 @@ -162,6 +164,12 @@ Icinga 2 still supports writing performance data files for graphing addons, but capability of writing performance data directly into a Graphite TCP socket simplifying realtime monitoring graphs. +* Native support for writing log events to [GELF](#gelf-writer) receivers (graylog2, Logstash) + +Icinga 2 will write all check result, state change and notification event logs into a defined +[GELF](#gelfwriter) input receiver. Natively provided by [graylog2](http://www.graylog2.org), +and as additional input type provided by [Logstash](http://logstash.net). + * Dynamic configuration language Simple [apply](#using-apply) and [assign](#group-assign) rules for creating configuration object diff --git a/doc/4-monitoring-basics.md b/doc/4-monitoring-basics.md index fcca50ba2..fdf8e278e 100644 --- a/doc/4-monitoring-basics.md +++ b/doc/4-monitoring-basics.md @@ -2171,7 +2171,7 @@ You can enable the feature using # icinga2 feature enable graphite By default the `GraphiteWriter` object expects the Graphite Carbon Cache to listen at -`127.0.0.1` on port `2003`. +`127.0.0.1` on TCP port `2003`. The current naming schema is @@ -2228,6 +2228,29 @@ Cache. Please make sure that the order is correct because the first match wins. pattern = ^icinga\. retentions = 1m:2d,5m:10d,30m:90d,360m:4y +### GELF Writer + +The `Graylog Extended Log Format` (short: [GELF](http://www.graylog2.org/resources/gelf)) +can be used to send application logs directly to a TCP socket. + +While it has been specified by the [graylog2](http://www.graylog2.org/) project as their +[input resource standard](http://www.graylog2.org/resources/gelf), other tools such as +[Logstash](http://www.logstash.net) also support `GELF` as +[input type](http://logstash.net/docs/latest/inputs/gelf). + +You can enable the feature using + + # icinga2 feature enable gelf + +By default the `GelfWriter` object expects the GELF receiver to listen at `127.0.0.1` on TCP port `12201`. +The default `source` attribute is set to `icinga2`. You can customize that for your needs if required. + +Currently these events are processed: +* Check results +* State changes +* Notifications + + ## Status Data Icinga 1.x writes object configuration data and status data in a cyclic diff --git a/doc/7-configuring-icinga-2.md b/doc/7-configuring-icinga-2.md index b883cf808..0b23d2933 100644 --- a/doc/7-configuring-icinga-2.md +++ b/doc/7-configuring-icinga-2.md @@ -1335,6 +1335,28 @@ Example with your custom [global constant](#global-constants) `GraphiteEnv`: host_name_template = GraphiteEnv + ".$host.name$" service_name_template = GraphiteEnv + ".$host.name$.$service.name$" +### GelfWriter + +Writes event log entries to a defined GELF receiver host (Graylog2, Logstash). + +Example: + + library "perfdata" + + object GelfWriter "gelf" { + host = "127.0.0.1" + port = 12201 + } + +Attributes: + + Name |Description + ----------------------|---------------------- + host |**Optional.** GELF receiver host address. Defaults to '127.0.0.1'. + port |**Optional.** GELF receiver port. Defaults to `12201`. + source |**Optional.** Source name for this instance. Defaults to `icinga2`. + + ### IdoMySqlConnection IDO database adapter for MySQL. diff --git a/etc/icinga2/features-available/gelf.conf b/etc/icinga2/features-available/gelf.conf index a9d180b51..43ea7aae5 100644 --- a/etc/icinga2/features-available/gelf.conf +++ b/etc/icinga2/features-available/gelf.conf @@ -1,11 +1,12 @@ /** - * The GelfWriter type writes event logs - * to a gelf tcp socket provided by graylog2. + * The GelfWriter type writes event log entries + * to a GELF tcp socket provided by graylog2, + * logstash or any other receiver. */ library "perfdata" -object GelfWriter "graylog2" { +object GelfWriter "gelf" { //host = "127.0.0.1" //port = 12201 }