]> granicus.if.org Git - icinga2/blob - components/perfdata/graphitewriter.cpp
Implement GetEnablePerfdata/GetEnableEventHandlers.
[icinga2] / components / perfdata / graphitewriter.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2013 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 "perfdata/graphitewriter.h"
21 #include "icinga/service.h"
22 #include "icinga/macroprocessor.h"
23 #include "icinga/icingaapplication.h"
24 #include "icinga/compatutility.h"
25 #include "base/tcpsocket.h"
26 #include "base/dynamictype.h"
27 #include "base/objectlock.h"
28 #include "base/logger_fwd.h"
29 #include "base/convert.h"
30 #include "base/utility.h"
31 #include "base/application.h"
32 #include "base/stream.h"
33 #include "base/networkstream.h"
34 #include "base/bufferedstream.h"
35 #include <boost/algorithm/string/classification.hpp>
36 #include <boost/smart_ptr/make_shared.hpp>
37 #include <boost/foreach.hpp>
38 #include <boost/algorithm/string/split.hpp>
39 #include <boost/algorithm/string/replace.hpp>
40
41 using namespace icinga;
42
43 REGISTER_TYPE(GraphiteWriter);
44
45 void GraphiteWriter::Start(void)
46 {
47         DynamicObject::Start();
48
49         m_ReconnectTimer = boost::make_shared<Timer>();
50         m_ReconnectTimer->SetInterval(10);
51         m_ReconnectTimer->OnTimerExpired.connect(boost::bind(&GraphiteWriter::ReconnectTimerHandler, this));
52         m_ReconnectTimer->Start();
53         m_ReconnectTimer->Reschedule(0);
54
55         Service::OnNewCheckResult.connect(boost::bind(&GraphiteWriter::CheckResultHandler, this, _1, _2));
56 }
57
58 String GraphiteWriter::GetHost(void) const
59 {
60         if (m_Host.IsEmpty())
61                 return "127.0.0.1";
62         else
63                 return m_Host;
64 }
65
66 String GraphiteWriter::GetPort(void) const
67 {
68         if (m_Port.IsEmpty())
69                 return "2003";
70         else
71                 return m_Port;
72 }
73
74 void GraphiteWriter::ReconnectTimerHandler(void)
75 {
76         try {
77                 if (m_Stream) {
78                         m_Stream->Write("\n", 1);
79                         Log(LogWarning, "perfdata", "GraphiteWriter already connected on socket on host '" + GetHost() + "' port '" + GetPort() + "'.");
80                         return;
81                 }
82         } catch (const std::exception& ex) {
83                 Log(LogWarning, "perfdata", "GraphiteWriter socket on host '" + GetHost() + "' port '" + GetPort() + "' gone. Attempting to reconnect.");       
84         }
85
86         TcpSocket::Ptr socket = boost::make_shared<TcpSocket>();
87
88         Log(LogInformation, "perfdata", "GraphiteWriter: Reconnect to tcp socket on host '" + GetHost() + "' port '" + GetPort() + "'.");
89         socket->Connect(GetHost(), GetPort());
90
91         NetworkStream::Ptr net_stream = boost::make_shared<NetworkStream>(socket);
92         m_Stream = boost::make_shared<BufferedStream>(net_stream);
93 }
94
95 void GraphiteWriter::CheckResultHandler(const Service::Ptr& service, const Dictionary::Ptr& cr)
96 {
97         if (!IcingaApplication::GetInstance()->GetEnablePerfdata() || !service->GetEnablePerfdata())
98                 return;
99
100         Host::Ptr host = service->GetHost();
101
102         if (!host)
103                 return;
104
105         /* service metrics */
106         std::vector<String> metrics;
107         String metricName;
108         Value metricValue;
109
110         /* basic metrics */
111         AddServiceMetric(metrics, service, "current_attempt", service->GetCurrentCheckAttempt());
112         AddServiceMetric(metrics, service, "max_check_attempts", service->GetMaxCheckAttempts());
113         AddServiceMetric(metrics, service, "state_type", service->GetStateType());
114         AddServiceMetric(metrics, service, "state", service->GetState());
115         AddServiceMetric(metrics, service, "latency", Service::CalculateLatency(cr));
116         AddServiceMetric(metrics, service, "execution_time", Service::CalculateExecutionTime(cr));
117
118         /* performance data metrics */
119         String perfdata = CompatUtility::GetCheckResultPerfdata(cr);
120
121         if (!perfdata.IsEmpty()) {
122                 Log(LogDebug, "perfdata", "GraphiteWriter: Processing perfdata: '" + perfdata + "'.");
123
124                 /*
125                  * 'foo bar'=0;;; baz=0.0;;;
126                  * 'label'=value[UOM];[warn];[crit];[min];[max]
127                 */
128                 std::vector<String> tokens;
129                 boost::algorithm::split(tokens, perfdata, boost::is_any_of(" "));
130
131                 /* TODO deal with 'foo bar'=0;;; 'baz'=1.0;;; */
132                 BOOST_FOREACH(const String& token, tokens) {
133                         std::vector<String> key_val;
134                         boost::algorithm::split(key_val, token, boost::is_any_of("="));
135
136                         if (key_val.size() == 0) {
137                                 Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data: '" + token + "'.");
138                                 return;
139                         }
140
141                         String metricName = key_val[0];
142
143                         if (key_val.size() == 1) {
144                                 Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data: '" + token + "'.");
145                                 return;
146                         }
147
148                         std::vector<String> perfdata_values;
149                         boost::algorithm::split(perfdata_values, key_val[1], boost::is_any_of(";"));
150
151                         if (perfdata_values.size() == 0) {
152                                 Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data: '" + token + "'.");
153                                 return;
154                         }
155
156                         /* TODO remove UOM from value */
157                         String metricValue = perfdata_values[0];
158
159                         /* //TODO: Figure out how graphite handles warn/crit/min/max
160                         String metricValueWarn, metricValueCrit, metricValueMin, metricValueMax;
161
162                         if (perfdata_values.size() > 1)
163                                 metricValueWarn = perfdata_values[1];
164                         if (perfdata_values.size() > 2)
165                                 metricValueCrit = perfdata_values[2];
166                         if (perfdata_values.size() > 3)
167                                 metricValueMin = perfdata_values[3];
168                         if (perfdata_values.size() > 4)
169                                 metricValueMax = perfdata_values[4];
170                         */
171
172                         AddServiceMetric(metrics, service, metricName, metricValue);
173                 }
174         }
175
176         SendMetrics(metrics);
177 }
178
179 void GraphiteWriter::AddServiceMetric(std::vector<String>& metrics, const Service::Ptr& service, const String& name, const Value& value)
180 {
181         /* TODO: sanitize host and service names */
182         String hostName = service->GetHost()->GetName();
183         String serviceName = service->GetShortName();   
184         String metricPrefix = hostName + "." + serviceName;
185         
186         boost::replace_all(metricPrefix, " ", "_");
187         boost::replace_all(metricPrefix, "-", "_");
188         boost::replace_all(metricPrefix, ".", "_");
189         boost::replace_all(metricPrefix, "\\", "_");
190         boost::replace_all(metricPrefix, "/", "_");
191         
192         String graphitePrefix = "icinga";
193
194         String metric = graphitePrefix + "." + metricPrefix + "." + name + " " + Convert::ToString(value) + " " + Convert::ToString(static_cast<long>(Utility::GetTime())) + "\n";
195         Log(LogDebug, "perfdata", "GraphiteWriter: Add to metric list:'" + metric + "'.");
196         metrics.push_back(metric);
197 }
198
199 void GraphiteWriter::SendMetrics(const std::vector<String>& metrics)
200 {
201         if (!m_Stream) {
202                 Log(LogWarning, "perfdata", "GraphiteWriter not connected!");
203                 return;
204         }
205
206         BOOST_FOREACH(const String& metric, metrics) {
207                 if (metric.IsEmpty())
208                         continue;
209
210                 Log(LogDebug, "perfdata", "GraphiteWriter: Sending metric '" + metric + "'.");
211                 m_Stream->Write(metric.CStr(), metric.GetLength());
212         }
213 }
214
215 void GraphiteWriter::InternalSerialize(const Dictionary::Ptr& bag, int attributeTypes) const
216 {
217         DynamicObject::InternalSerialize(bag, attributeTypes);
218
219         if (attributeTypes & Attribute_Config) {
220                 bag->Set("host", m_Host);
221                 bag->Set("port", m_Port);
222         }
223 }
224
225 void GraphiteWriter::InternalDeserialize(const Dictionary::Ptr& bag, int attributeTypes)
226 {
227         DynamicObject::InternalDeserialize(bag, attributeTypes);
228
229         if (attributeTypes & Attribute_Config) {
230                 m_Host = bag->Get("host");
231                 m_Port = bag->Get("port");
232         }
233 }