2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "dnsdist-carbon.hh"
29 #ifndef DISABLE_CARBON
32 #include "threadname.hh"
34 GlobalStateHolder
<vector
<CarbonConfig
> > g_carbon
;
36 void carbonDumpThread()
40 setThreadName("dnsdist/carbon");
41 auto localCarbon
= g_carbon
.getLocal();
42 for(int numloops
=0;;++numloops
) {
43 if(localCarbon
->empty()) {
47 /* this is wrong, we use the interval of the first server
48 for every single one of them */
50 const unsigned int interval
= localCarbon
->at(0).interval
;
54 for (const auto& conf
: *localCarbon
) {
55 const auto& server
= conf
.server
;
56 const std::string
& namespace_name
= conf
.namespace_name
;
57 std::string hostname
= conf
.ourname
;
58 if (hostname
.empty()) {
60 hostname
= getCarbonHostName();
62 catch(const std::exception
& e
) {
63 throw std::runtime_error(std::string("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: ") + e
.what());
66 const std::string
& instance_name
= conf
.instance_name
;
69 Socket
s(server
.sin4
.sin_family
, SOCK_STREAM
);
71 s
.connect(server
); // we do the connect so the attempt happens while we gather stats
74 for(const auto& e
: g_stats
.entries
) {
75 str
<<namespace_name
<<"."<<hostname
<<"."<<instance_name
<<"."<<e
.first
<<' ';
76 if(const auto& val
= boost::get
<pdns::stat_t
*>(&e
.second
))
78 else if (const auto& dval
= boost::get
<double*>(&e
.second
))
81 str
<<(*boost::get
<DNSDistStats::statfunction_t
>(&e
.second
))(e
.first
);
82 str
<<' '<<now
<<"\r\n";
84 auto states
= g_dstates
.getLocal();
85 for(const auto& state
: *states
) {
86 string serverName
= state
->getName().empty() ? state
->d_config
.remote
.toStringWithPort() : state
->getName();
87 boost::replace_all(serverName
, ".", "_");
88 const string base
= namespace_name
+ "." + hostname
+ "." + instance_name
+ ".servers." + serverName
+ ".";
89 str
<<base
<<"queries" << ' ' << state
->queries
.load() << " " << now
<< "\r\n";
90 str
<<base
<<"responses" << ' ' << state
->responses
.load() << " " << now
<< "\r\n";
91 str
<<base
<<"drops" << ' ' << state
->reuseds
.load() << " " << now
<< "\r\n";
92 str
<<base
<<"latency" << ' ' << (state
->d_config
.availability
!= DownstreamState::Availability::Down
? state
->latencyUsec
/1000.0 : 0) << " " << now
<< "\r\n";
93 str
<<base
<<"senderrors" << ' ' << state
->sendErrors
.load() << " " << now
<< "\r\n";
94 str
<<base
<<"outstanding" << ' ' << state
->outstanding
.load() << " " << now
<< "\r\n";
95 str
<<base
<<"tcpdiedsendingquery" << ' '<< state
->tcpDiedSendingQuery
.load() << " " << now
<< "\r\n";
96 str
<<base
<<"tcpdiedreaddingresponse" << ' '<< state
->tcpDiedReadingResponse
.load() << " " << now
<< "\r\n";
97 str
<<base
<<"tcpgaveup" << ' '<< state
->tcpGaveUp
.load() << " " << now
<< "\r\n";
98 str
<<base
<<"tcpreadimeouts" << ' '<< state
->tcpReadTimeouts
.load() << " " << now
<< "\r\n";
99 str
<<base
<<"tcpwritetimeouts" << ' '<< state
->tcpWriteTimeouts
.load() << " " << now
<< "\r\n";
100 str
<<base
<<"tcpconnecttimeouts" << ' '<< state
->tcpConnectTimeouts
.load() << " " << now
<< "\r\n";
101 str
<<base
<<"tcpcurrentconnections" << ' '<< state
->tcpCurrentConnections
.load() << " " << now
<< "\r\n";
102 str
<<base
<<"tcpmaxconcurrentconnections" << ' '<< state
->tcpMaxConcurrentConnections
.load() << " " << now
<< "\r\n";
103 str
<<base
<<"tcpnewconnections" << ' '<< state
->tcpNewConnections
.load() << " " << now
<< "\r\n";
104 str
<<base
<<"tcpreusedconnections" << ' '<< state
->tcpReusedConnections
.load() << " " << now
<< "\r\n";
105 str
<<base
<<"tlsresumptions" << ' '<< state
->tlsResumptions
.load() << " " << now
<< "\r\n";
106 str
<<base
<<"tcpavgqueriesperconnection" << ' '<< state
->tcpAvgQueriesPerConnection
.load() << " " << now
<< "\r\n";
107 str
<<base
<<"tcpavgconnectionduration" << ' '<< state
->tcpAvgConnectionDuration
.load() << " " << now
<< "\r\n";
110 std::map
<std::string
,uint64_t> frontendDuplicates
;
111 for(const auto& front
: g_frontends
) {
112 if (front
->udpFD
== -1 && front
->tcpFD
== -1)
115 string frontName
= front
->local
.toStringWithPort() + (front
->udpFD
>= 0 ? "_udp" : "_tcp");
116 boost::replace_all(frontName
, ".", "_");
117 auto dupPair
= frontendDuplicates
.insert({frontName
, 1});
118 if (!dupPair
.second
) {
119 frontName
= frontName
+ "_" + std::to_string(dupPair
.first
->second
);
120 ++(dupPair
.first
->second
);
123 const string base
= namespace_name
+ "." + hostname
+ "." + instance_name
+ ".frontends." + frontName
+ ".";
124 str
<<base
<<"queries" << ' ' << front
->queries
.load() << " " << now
<< "\r\n";
125 str
<<base
<<"responses" << ' ' << front
->responses
.load() << " " << now
<< "\r\n";
126 str
<<base
<<"tcpdiedreadingquery" << ' '<< front
->tcpDiedReadingQuery
.load() << " " << now
<< "\r\n";
127 str
<<base
<<"tcpdiedsendingresponse" << ' '<< front
->tcpDiedSendingResponse
.load() << " " << now
<< "\r\n";
128 str
<<base
<<"tcpgaveup" << ' '<< front
->tcpGaveUp
.load() << " " << now
<< "\r\n";
129 str
<<base
<<"tcpclientimeouts" << ' '<< front
->tcpClientTimeouts
.load() << " " << now
<< "\r\n";
130 str
<<base
<<"tcpdownstreamtimeouts" << ' '<< front
->tcpDownstreamTimeouts
.load() << " " << now
<< "\r\n";
131 str
<<base
<<"tcpcurrentconnections" << ' '<< front
->tcpCurrentConnections
.load() << " " << now
<< "\r\n";
132 str
<<base
<<"tcpmaxconcurrentconnections" << ' '<< front
->tcpMaxConcurrentConnections
.load() << " " << now
<< "\r\n";
133 str
<<base
<<"tcpavgqueriesperconnection" << ' '<< front
->tcpAvgQueriesPerConnection
.load() << " " << now
<< "\r\n";
134 str
<<base
<<"tcpavgconnectionduration" << ' '<< front
->tcpAvgConnectionDuration
.load() << " " << now
<< "\r\n";
135 str
<<base
<<"tls10-queries" << ' ' << front
->tls10queries
.load() << " " << now
<< "\r\n";
136 str
<<base
<<"tls11-queries" << ' ' << front
->tls11queries
.load() << " " << now
<< "\r\n";
137 str
<<base
<<"tls12-queries" << ' ' << front
->tls12queries
.load() << " " << now
<< "\r\n";
138 str
<<base
<<"tls13-queries" << ' ' << front
->tls13queries
.load() << " " << now
<< "\r\n";
139 str
<<base
<<"tls-unknown-queries" << ' ' << front
->tlsUnknownqueries
.load() << " " << now
<< "\r\n";
140 str
<<base
<<"tlsnewsessions" << ' ' << front
->tlsNewSessions
.load() << " " << now
<< "\r\n";
141 str
<<base
<<"tlsresumptions" << ' ' << front
->tlsResumptions
.load() << " " << now
<< "\r\n";
142 str
<<base
<<"tlsunknownticketkeys" << ' ' << front
->tlsUnknownTicketKey
.load() << " " << now
<< "\r\n";
143 str
<<base
<<"tlsinactiveticketkeys" << ' ' << front
->tlsInactiveTicketKey
.load() << " " << now
<< "\r\n";
144 const TLSErrorCounters
* errorCounters
= nullptr;
145 if (front
->tlsFrontend
!= nullptr) {
146 errorCounters
= &front
->tlsFrontend
->d_tlsCounters
;
148 else if (front
->dohFrontend
!= nullptr) {
149 errorCounters
= &front
->dohFrontend
->d_tlsCounters
;
151 if (errorCounters
!= nullptr) {
152 str
<<base
<<"tlsdhkeytoosmall" << ' ' << errorCounters
->d_dhKeyTooSmall
<< " " << now
<< "\r\n";
153 str
<<base
<<"tlsinappropriatefallback" << ' ' << errorCounters
->d_inappropriateFallBack
<< " " << now
<< "\r\n";
154 str
<<base
<<"tlsnosharedcipher" << ' ' << errorCounters
->d_noSharedCipher
<< " " << now
<< "\r\n";
155 str
<<base
<<"tlsunknownciphertype" << ' ' << errorCounters
->d_unknownCipherType
<< " " << now
<< "\r\n";
156 str
<<base
<<"tlsunknownkeyexchangetype" << ' ' << errorCounters
->d_unknownKeyExchangeType
<< " " << now
<< "\r\n";
157 str
<<base
<<"tlsunknownprotocol" << ' ' << errorCounters
->d_unknownProtocol
<< " " << now
<< "\r\n";
158 str
<<base
<<"tlsunsupportedec" << ' ' << errorCounters
->d_unsupportedEC
<< " " << now
<< "\r\n";
159 str
<<base
<<"tlsunsupportedprotocol" << ' ' << errorCounters
->d_unsupportedProtocol
<< " " << now
<< "\r\n";
163 auto localPools
= g_pools
.getLocal();
164 for (const auto& entry
: *localPools
) {
165 string poolName
= entry
.first
;
166 boost::replace_all(poolName
, ".", "_");
167 if (poolName
.empty()) {
168 poolName
= "_default_";
170 const string base
= namespace_name
+ "." + hostname
+ "." + instance_name
+ ".pools." + poolName
+ ".";
171 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
172 str
<<base
<<"servers" << " " << pool
->countServers(false) << " " << now
<< "\r\n";
173 str
<<base
<<"servers-up" << " " << pool
->countServers(true) << " " << now
<< "\r\n";
174 if (pool
->packetCache
!= nullptr) {
175 const auto& cache
= pool
->packetCache
;
176 str
<<base
<<"cache-size" << " " << cache
->getMaxEntries() << " " << now
<< "\r\n";
177 str
<<base
<<"cache-entries" << " " << cache
->getEntriesCount() << " " << now
<< "\r\n";
178 str
<<base
<<"cache-hits" << " " << cache
->getHits() << " " << now
<< "\r\n";
179 str
<<base
<<"cache-misses" << " " << cache
->getMisses() << " " << now
<< "\r\n";
180 str
<<base
<<"cache-deferred-inserts" << " " << cache
->getDeferredInserts() << " " << now
<< "\r\n";
181 str
<<base
<<"cache-deferred-lookups" << " " << cache
->getDeferredLookups() << " " << now
<< "\r\n";
182 str
<<base
<<"cache-lookup-collisions" << " " << cache
->getLookupCollisions() << " " << now
<< "\r\n";
183 str
<<base
<<"cache-insert-collisions" << " " << cache
->getInsertCollisions() << " " << now
<< "\r\n";
184 str
<<base
<<"cache-ttl-too-shorts" << " " << cache
->getTTLTooShorts() << " " << now
<< "\r\n";
188 #ifdef HAVE_DNS_OVER_HTTPS
190 std::map
<std::string
,uint64_t> dohFrontendDuplicates
;
191 const string base
= "dnsdist." + hostname
+ ".main.doh.";
192 for(const auto& doh
: g_dohlocals
) {
193 string name
= doh
->d_local
.toStringWithPort();
194 boost::replace_all(name
, ".", "_");
195 boost::replace_all(name
, ":", "_");
196 boost::replace_all(name
, "[", "_");
197 boost::replace_all(name
, "]", "_");
199 auto dupPair
= dohFrontendDuplicates
.insert({name
, 1});
200 if (!dupPair
.second
) {
201 name
= name
+ "_" + std::to_string(dupPair
.first
->second
);
202 ++(dupPair
.first
->second
);
205 vector
<pair
<const char*, const pdns::stat_t
&>> v
{
206 {"http-connects", doh
->d_httpconnects
},
207 {"http1-queries", doh
->d_http1Stats
.d_nbQueries
},
208 {"http2-queries", doh
->d_http2Stats
.d_nbQueries
},
209 {"http1-200-responses", doh
->d_http1Stats
.d_nb200Responses
},
210 {"http2-200-responses", doh
->d_http2Stats
.d_nb200Responses
},
211 {"http1-400-responses", doh
->d_http1Stats
.d_nb400Responses
},
212 {"http2-400-responses", doh
->d_http2Stats
.d_nb400Responses
},
213 {"http1-403-responses", doh
->d_http1Stats
.d_nb403Responses
},
214 {"http2-403-responses", doh
->d_http2Stats
.d_nb403Responses
},
215 {"http1-500-responses", doh
->d_http1Stats
.d_nb500Responses
},
216 {"http2-500-responses", doh
->d_http2Stats
.d_nb500Responses
},
217 {"http1-502-responses", doh
->d_http1Stats
.d_nb502Responses
},
218 {"http2-502-responses", doh
->d_http2Stats
.d_nb502Responses
},
219 {"http1-other-responses", doh
->d_http1Stats
.d_nbOtherResponses
},
220 {"http2-other-responses", doh
->d_http2Stats
.d_nbOtherResponses
},
221 {"get-queries", doh
->d_getqueries
},
222 {"post-queries", doh
->d_postqueries
},
223 {"bad-requests", doh
->d_badrequests
},
224 {"error-responses", doh
->d_errorresponses
},
225 {"redirect-responses", doh
->d_redirectresponses
},
226 {"valid-responses", doh
->d_validresponses
}
229 for(const auto& item
: v
) {
230 str
<<base
<<name
<<"."<<item
.first
<< " " << item
.second
<< " " << now
<<"\r\n";
234 #endif /* HAVE_DNS_OVER_HTTPS */
238 auto records
= g_qcount
.records
.write_lock();
239 for (const auto &record
: *records
) {
240 qname
= record
.first
;
241 boost::replace_all(qname
, ".", "_");
242 str
<<"dnsdist.querycount." << qname
<< ".queries " << record
.second
<< " " << now
<< "\r\n";
247 const string msg
= str
.str();
249 int ret
= waitForRWData(s
.getHandle(), false, 1 , 0);
251 vinfolog("Unable to write data to carbon server on %s: %s", server
.toStringWithPort(), (ret
<0 ? stringerror() : "Timeout"));
255 writen2(s
.getHandle(), msg
.c_str(), msg
.size());
257 catch(const std::exception
& e
) {
258 warnlog("Problem sending carbon data: %s", e
.what());
263 catch(const std::exception
& e
)
265 errlog("Carbon thread died: %s", e
.what());
267 catch(const PDNSException
& e
)
269 errlog("Carbon thread died, PDNSException: %s", e
.reason
);
273 errlog("Carbon thread died");
276 #endif /* DISABLE_CARBON */
278 static time_t s_start
= time(nullptr);
280 uint64_t uptimeOfProcess(const std::string
& str
)
282 return time(nullptr) - s_start
;