* `topQueries(20)`: shows the top-20 queries
* `topQueries(20,2)`: shows the top-20 two-level domain queries (so `topQueries(20,1)` only shows TLDs)
* `topResponses(20, 2)`: top-20 servfail responses (use ,3 for NXDOMAIN)
- * `grepq(Netmask|DNS Name [, n])`: shows the queries and responses matching the specified client address or range (Netmask), or the specified DNS Name
- * `topBandwidth(top)`: get top-`top` clients that consume the most bandwidth over length of ringbuffer
+ * `grepq(Netmask|DNS Name|100ms [, n])`: shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms
+ * `grepq({"::1", "powerdns.com", "100ms"} [, n])`: shows the last n queries and responses matching the specified client address AND range (Netmask) AND the specified DNS Name AND slower than 100ms
+ * `topBandwidth(top)`: show top-`top` clients that consume the most bandwidth over length of ringbuffer
+ * `topSlow([top][, limit][, labels])`: show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels
For example:
```
});
- g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+ g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
g_lua.writeFunction("showResponseLatency", []() {
}
});
- g_lua.writeFunction("grepq", [](const std::string& s, boost::optional<unsigned int> limit) {
+ g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
boost::optional<Netmask> nm;
boost::optional<DNSName> dn;
- unsigned int msec=0;
- try
- {
- nm = Netmask(s);
+ int msec=-1;
+
+ vector<string> vec;
+ auto str=boost::get<string>(&inp);
+ if(str)
+ vec.push_back(*str);
+ else {
+ auto v = boost::get<vector<pair<int, string> > >(inp);
+ for(const auto& a: v)
+ vec.push_back(a.second);
}
- catch(...) {
- if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
- ;
- }
- else {
- try { dn=DNSName(s); }
- catch(...)
- {
- g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
- return;
- }
+
+ for(const auto& s : vec) {
+ try
+ {
+ nm = Netmask(s);
+ }
+ catch(...) {
+ if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
+ ;
+ }
+ else {
+ try { dn=DNSName(s); }
+ catch(...)
+ {
+ g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
+ return;
+ }
+ }
}
}
std::multimap<struct timespec, string> out;
- boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-4.1f %-2s %-2s %-2s %s\n");
+ boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
- for(const auto& c : qr) {
- if((nm && nm->match(c.requestor)) || (dn && c.name.isPartOf(*dn)) ) {
- QType qt(c.qtype);
- out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
-
- if(limit && *limit==++num)
- break;
+ if(msec==-1) {
+ for(const auto& c : qr) {
+ bool nmmatch=true, dnmatch=true;
+ if(nm)
+ nmmatch = nm->match(c.requestor);
+ if(dn)
+ dnmatch = c.name.isPartOf(*dn);
+ if(nmmatch && dnmatch) {
+ QType qt(c.qtype);
+ out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
+
+ if(limit && *limit==++num)
+ break;
+ }
}
}
num=0;
string extra;
for(const auto& c : rr) {
- if((nm && nm->match(c.requestor)) || (dn && c.name.isPartOf(*dn)) || (msec && (c.usec/1000 > msec)) ) {
+ bool nmmatch=true, dnmatch=true, msecmatch=true;
+ if(nm)
+ nmmatch = nm->match(c.requestor);
+ if(dn)
+ dnmatch = c.name.isPartOf(*dn);
+ if(msec != -1)
+ msecmatch=(c.usec/1000 > (unsigned int)msec);
+
+ if(nmmatch && dnmatch && msecmatch) {
QType qt(c.qtype);
if(!c.dh.rcode)
extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";