]> git.ipfire.org Git - thirdparty/squid.git/blame - src/log/access_log.cc
SourceLayout: Move time related tools to time/libtime.la (#1001)
[thirdparty/squid.git] / src / log / access_log.cc
CommitLineData
f892c2bf 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
f892c2bf 7 */
8
bbc27441
AJ
9/* DEBUG: section 46 Access Log */
10
582c2af2 11#include "squid.h"
450e0c10 12#include "AccessLogEntry.h"
3ad63615 13#include "acl/Checklist.h"
cf1f23ee 14#include "sbuf/Algorithms.h"
d7f4a0b7
CT
15#if USE_ADAPTATION
16#include "adaptation/Config.h"
17#endif
cf1f23ee 18#include "base/PackableStream.h"
a011edee 19#include "CachePeer.h"
83b053a0 20#include "error/Detail.h"
a011edee 21#include "errorpage.h"
31971e6a 22#include "format/Token.h"
582c2af2 23#include "globals.h"
bbaf2685 24#include "hier_code.h"
7684c4b1 25#include "HttpReply.h"
26#include "HttpRequest.h"
1c7ae5ff 27#include "log/access_log.h"
38e16f92 28#include "log/Config.h"
8f645002 29#include "log/CustomLog.h"
82b7abe3 30#include "log/File.h"
20efa1c2 31#include "log/Formats.h"
0eb49b6d 32#include "MemBuf.h"
8822ebee 33#include "mgr/Registration.h"
1fa9b1a7 34#include "rfc1738.h"
cf1f23ee 35#include "sbuf/SBuf.h"
4d5904f7 36#include "SquidConfig.h"
a011edee 37#include "Store.h"
7684c4b1 38
3d41e53a
FC
39#if USE_SQUID_EUI
40#include "eui/Eui48.h"
41#include "eui/Eui64.h"
42#endif
43
cf1f23ee
FC
44#include <unordered_map>
45
c3609322 46#if HEADERS_LOG
47static Logfile *headerslog = NULL;
48#endif
a7c05555 49
e66d7923 50#if MULTICAST_MISS_STREAM
51static int mcast_miss_fd = -1;
62e76326 52
e66d7923 53static struct sockaddr_in mcast_miss_to;
9bea1d5b 54static void mcast_encode(unsigned int *, size_t, const unsigned int *);
e66d7923 55#endif
56
20efa1c2 57#if USE_FORW_VIA_DB
3ff65596 58
2081cefa 59using HeaderValueCountsElement = std::pair<const SBuf, uint64_t>;
cf1f23ee
FC
60/// counts the number of header field value occurrences
61using HeaderValueCounts = std::unordered_map<SBuf, uint64_t, std::hash<SBuf>, std::equal_to<SBuf>, PoolingAllocator<HeaderValueCountsElement> >;
62
63/// counts the number of HTTP Via header field value occurrences
64static HeaderValueCounts TheViaCounts;
9386f99d
FC
65/// counts the number of HTTP X-Forwarded-For header field value occurrences
66static HeaderValueCounts TheForwardedCounts;
cf1f23ee 67
20efa1c2 68static OBJH fvdbDumpVia;
9386f99d 69static OBJH fvdbDumpForwarded;
20efa1c2
AJ
70static void fvdbClear(void);
71static void fvdbRegisterWithCacheManager();
3ff65596
AR
72#endif
73
20efa1c2 74int LogfileStatus = LOG_DISABLE;
3ff65596 75
e1381638 76void
63ed9e8e 77accessLogLogTo(CustomLog *log, const AccessLogEntryPointer &al, ACLChecklist *checklist)
3ff65596 78{
62e76326 79
f57ae909
NH
80 if (al->url.isEmpty())
81 al->url = Format::Dash;
62e76326 82
9bea1d5b 83 if (!al->http.content_type || *al->http.content_type == '\0')
62e76326 84 al->http.content_type = dash_str;
85
9bea1d5b 86 if (al->hier.host[0] == '\0')
62e76326 87 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
9bea1d5b 88
3ff65596 89 for (; log; log = log->next) {
06bf5384 90 if (log->aclList && checklist && !checklist->fastCheck(log->aclList).allowed())
7684c4b1 91 continue;
62e76326 92
80b2b3c5
AJ
93 // The special-case "none" type has no logfile object set
94 if (log->type == Log::Format::CLF_NONE)
95 return;
96
82b7abe3
AJ
97 if (log->logfile) {
98 logfileLineStart(log->logfile);
7684c4b1 99
82b7abe3 100 switch (log->type) {
7684c4b1 101
20efa1c2
AJ
102 case Log::Format::CLF_SQUID:
103 Log::Format::SquidNative(al, log->logfile);
104 break;
105
106 case Log::Format::CLF_COMBINED:
107 Log::Format::HttpdCombined(al, log->logfile);
108 break;
109
110 case Log::Format::CLF_COMMON:
111 Log::Format::HttpdCommon(al, log->logfile);
82b7abe3 112 break;
7684c4b1 113
20efa1c2
AJ
114 case Log::Format::CLF_REFERER:
115 Log::Format::SquidReferer(al, log->logfile);
82b7abe3 116 break;
7684c4b1 117
20efa1c2
AJ
118 case Log::Format::CLF_USERAGENT:
119 Log::Format::SquidUserAgent(al, log->logfile);
82b7abe3 120 break;
7684c4b1 121
20efa1c2
AJ
122 case Log::Format::CLF_CUSTOM:
123 Log::Format::SquidCustom(al, log);
82b7abe3 124 break;
7684c4b1 125
3ff65596 126#if ICAP_CLIENT
20efa1c2
AJ
127 case Log::Format::CLF_ICAP_SQUID:
128 Log::Format::SquidIcap(al, log->logfile);
82b7abe3 129 break;
3ff65596
AR
130#endif
131
82b7abe3
AJ
132 default:
133 fatalf("Unknown log format %d\n", log->type);
134 break;
135 }
7684c4b1 136
82b7abe3 137 logfileLineEnd(log->logfile);
7684c4b1 138 }
139
82b7abe3 140 // NP: WTF? if _any_ log line has no checklist ignore the following ones?
7684c4b1 141 if (!checklist)
142 break;
f892c2bf 143 }
3ff65596 144}
7684c4b1 145
3ff65596 146void
63ed9e8e 147accessLogLog(const AccessLogEntryPointer &al, ACLChecklist *checklist)
3ff65596
AR
148{
149 if (LogfileStatus != LOG_ENABLE)
150 return;
e1381638 151
3ff65596 152 accessLogLogTo(Config.Log.accesslogs, al, checklist);
e66d7923 153#if MULTICAST_MISS_STREAM
62e76326 154
9bea1d5b 155 if (al->cache.code != LOG_TCP_MISS)
62e76326 156 (void) 0;
9bea1d5b 157 else if (al->http.method != METHOD_GET)
62e76326 158 (void) 0;
9bea1d5b 159 else if (mcast_miss_fd < 0)
62e76326 160 (void) 0;
9bea1d5b 161 else {
62e76326 162 unsigned int ibuf[365];
163 size_t isize;
f57ae909
NH
164 xstrncpy((char *) ibuf, al->url.c_str(), 364 * sizeof(int));
165 isize = ((al->url.length() + 8) / 8) * 2;
62e76326 166
167 if (isize > 364)
168 isize = 364;
169
170 mcast_encode((unsigned int *) ibuf, isize,
171 (const unsigned int *) Config.mcast_miss.encode_key);
172
173 comm_udp_sendto(mcast_miss_fd,
174 &mcast_miss_to, sizeof(mcast_miss_to),
175 ibuf, isize * sizeof(int));
e66d7923 176 }
62e76326 177
e66d7923 178#endif
f892c2bf 179}
180
181void
9bea1d5b 182accessLogRotate(void)
f892c2bf 183{
87ddff6e 184 CustomLog *log;
21f6708d 185#if USE_FORW_VIA_DB
7684c4b1 186
9bea1d5b 187 fvdbClear();
d21f1c54 188#endif
62e76326 189
7684c4b1 190 for (log = Config.Log.accesslogs; log; log = log->next) {
39d7714a 191 log->rotate();
7684c4b1 192 }
62e76326 193
c3609322 194#if HEADERS_LOG
62e76326 195
efc23871 196 logfileRotate(headerslog, Config.Log.rotateNumber);
62e76326 197
c3609322 198#endif
f892c2bf 199}
200
201void
9bea1d5b 202accessLogClose(void)
f892c2bf 203{
87ddff6e 204 CustomLog *log;
62e76326 205
7684c4b1 206 for (log = Config.Log.accesslogs; log; log = log->next) {
207 if (log->logfile) {
208 logfileClose(log->logfile);
209 log->logfile = NULL;
210 }
211 }
62e76326 212
c3609322 213#if HEADERS_LOG
62e76326 214
9bea1d5b 215 logfileClose(headerslog);
62e76326 216
9bea1d5b 217 headerslog = NULL;
62e76326 218
c3609322 219#endif
f892c2bf 220}
221
b24880fe 222HierarchyLogEntry::HierarchyLogEntry() :
f53969cc
SM
223 code(HIER_NONE),
224 cd_lookup(LOOKUP_NONE),
225 n_choices(0),
226 n_ichoices(0),
227 peer_reply_status(Http::scNone),
228 tcpServer(NULL),
229 bodyBytesRead(-1)
b24880fe 230{
231 memset(host, '\0', SQUIDHOSTNAMELEN);
232 memset(cd_host, '\0', SQUIDHOSTNAMELEN);
233
234 peer_select_start.tv_sec =0;
235 peer_select_start.tv_usec =0;
236
237 store_complete_stop.tv_sec =0;
238 store_complete_stop.tv_usec =0;
3ff65596 239
d8165775 240 clearPeerNotes();
01bd87d8
CT
241
242 totalResponseTime_.tv_sec = -1;
243 totalResponseTime_.tv_usec = 0;
244
16b70e2a
CT
245 firstConnStart_.tv_sec = 0;
246 firstConnStart_.tv_usec = 0;
b24880fe 247}
248
f892c2bf 249void
d8165775 250HierarchyLogEntry::resetPeerNotes(const Comm::ConnectionPointer &server, const char *requestedHost)
f892c2bf 251{
d8165775
AR
252 clearPeerNotes();
253
a14f38d0
AJ
254 tcpServer = server;
255 if (tcpServer == NULL) {
c69ef286
A
256 code = HIER_NONE;
257 xstrncpy(host, requestedHost, sizeof(host));
a14f38d0
AJ
258 } else {
259 code = tcpServer->peerType;
260
261 if (tcpServer->getPeer()) {
262 // went to peer, log peer host name
263 xstrncpy(host, tcpServer->getPeer()->name, sizeof(host));
264 } else {
8652f8e7 265 xstrncpy(host, requestedHost, sizeof(host));
a14f38d0
AJ
266 }
267 }
f892c2bf 268}
7a2f978b 269
d8165775
AR
270/// forget previous notePeerRead() and notePeerWrite() calls (if any)
271void
272HierarchyLogEntry::clearPeerNotes()
273{
274 peer_last_read_.tv_sec = 0;
275 peer_last_read_.tv_usec = 0;
276
277 peer_last_write_.tv_sec = 0;
278 peer_last_write_.tv_usec = 0;
279
280 bodyBytesRead = -1;
281}
282
283void
284HierarchyLogEntry::notePeerRead()
285{
286 peer_last_read_ = current_time;
287}
288
289void
290HierarchyLogEntry::notePeerWrite()
291{
292 peer_last_write_ = current_time;
293}
294
16b70e2a
CT
295void
296HierarchyLogEntry::startPeerClock()
297{
298 if (!firstConnStart_.tv_sec)
299 firstConnStart_ = current_time;
300}
301
302void
bd1be2ae
A
303HierarchyLogEntry::stopPeerClock(const bool force)
304{
d8ee9e8d 305 debugs(46, 5, "First connection started: " << firstConnStart_ <<
01bd87d8 306 ", current total response time value: " << (totalResponseTime_.tv_sec * 1000 + totalResponseTime_.tv_usec/1000) <<
16b70e2a 307 (force ? ", force fixing" : ""));
01bd87d8 308 if (!force && totalResponseTime_.tv_sec != -1)
16b70e2a
CT
309 return;
310
01bd87d8
CT
311 if (firstConnStart_.tv_sec)
312 tvSub(totalResponseTime_, firstConnStart_, current_time);
16b70e2a
CT
313}
314
d8165775
AR
315bool
316HierarchyLogEntry::peerResponseTime(struct timeval &responseTime)
317{
318 // no I/O whatsoever
319 if (peer_last_write_.tv_sec <= 0 && peer_last_read_.tv_sec <= 0)
320 return false;
321
322 // accommodate read without (completed) write
323 const auto last_write = peer_last_write_.tv_sec > 0 ?
ee1d6f66 324 peer_last_write_ : peer_last_read_;
d8165775
AR
325
326 // accommodate write without (completed) read
327 const auto last_read = peer_last_read_.tv_sec > 0 ?
ee1d6f66 328 peer_last_read_ : peer_last_write_;
d8165775
AR
329
330 tvSub(responseTime, last_write, last_read);
331 // The peer response time (%<pt) stopwatch is currently defined to start
332 // when we wrote the entire request. Thus, if we wrote something after the
333 // last read, report zero peer response time.
334 if (responseTime.tv_sec < 0) {
335 responseTime.tv_sec = 0;
336 responseTime.tv_usec = 0;
337 }
338
339 return true;
340}
341
342bool
01bd87d8 343HierarchyLogEntry::totalResponseTime(struct timeval &responseTime)
16b70e2a
CT
344{
345 // This should not really happen, but there may be rare code
346 // paths that lead to FwdState discarded (or transaction logged)
347 // without (or before) a stopPeerClock() call.
01bd87d8 348 if (firstConnStart_.tv_sec && totalResponseTime_.tv_sec == -1)
16b70e2a
CT
349 stopPeerClock(false);
350
01bd87d8 351 responseTime = totalResponseTime_;
d8165775 352 return responseTime.tv_sec >= 0 && responseTime.tv_usec >= 0;
16b70e2a
CT
353}
354
5f5e883f
FC
355static void
356accessLogRegisterWithCacheManager(void)
357{
21f6708d 358#if USE_FORW_VIA_DB
5f5e883f
FC
359 fvdbRegisterWithCacheManager();
360#endif
361}
362
7a2f978b 363void
9bea1d5b 364accessLogInit(void)
7a2f978b 365{
87ddff6e 366 CustomLog *log;
caf0eda4
FC
367
368 accessLogRegisterWithCacheManager();
369
190d8192 370#if USE_ADAPTATION
38e16f92 371 Log::TheConfig.hasAdaptToken = false;
190d8192
AJ
372#endif
373#if ICAP_CLIENT
38e16f92 374 Log::TheConfig.hasIcapToken = false;
190d8192
AJ
375#endif
376
7684c4b1 377 for (log = Config.Log.accesslogs; log; log = log->next) {
20efa1c2 378 if (log->type == Log::Format::CLF_NONE)
7684c4b1 379 continue;
62e76326 380
fb0c2f17 381 log->logfile = logfileOpen(log->filename, log->bufferSize, log->fatal);
62e76326 382
7684c4b1 383 LogfileStatus = LOG_ENABLE;
3ff65596 384
3ff65596 385#if USE_ADAPTATION
38e16f92 386 for (Format::Token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) {
31971e6a
AJ
387 if (curr_token->type == Format::LFT_ADAPTATION_SUM_XACT_TIMES ||
388 curr_token->type == Format::LFT_ADAPTATION_ALL_XACT_TIMES ||
38e16f92
AJ
389 curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER ||
390 curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER_ELEM ||
d7f4a0b7
CT
391 curr_token->type == Format::LFT_ADAPTATION_LAST_ALL_HEADERS||
392 (curr_token->type == Format::LFT_NOTE && !Adaptation::Config::metaHeaders.empty())) {
38e16f92 393 Log::TheConfig.hasAdaptToken = true;
3ff65596 394 }
3ff65596 395#if ICAP_CLIENT
38e16f92
AJ
396 if (curr_token->type == Format::LFT_ICAP_TOTAL_TIME) {
397 Log::TheConfig.hasIcapToken = true;
3ff65596
AR
398 }
399#endif
400 }
401#endif
7684c4b1 402 }
62e76326 403
c3609322 404#if HEADERS_LOG
62e76326 405
9bea1d5b 406 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
62e76326 407
9bea1d5b 408 assert(NULL != headerslog);
62e76326 409
d21f1c54 410#endif
e66d7923 411#if MULTICAST_MISS_STREAM
62e76326 412
9bea1d5b 413 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
62e76326 414 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
415 mcast_miss_to.sin_family = AF_INET;
416 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
417 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
418 mcast_miss_fd = comm_open(SOCK_DGRAM,
bdb741f4 419 IPPROTO_UDP,
62e76326 420 Config.Addrs.udp_incoming,
421 Config.mcast_miss.port,
422 COMM_NONBLOCKING,
423 "Multicast Miss Stream");
424
425 if (mcast_miss_fd < 0)
426 fatal("Cannot open Multicast Miss Stream Socket");
427
e0236918 428 debugs(46, DBG_IMPORTANT, "Multicast Miss Stream Socket opened on FD " << mcast_miss_fd);
62e76326 429
430 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
431
432 if (strlen(Config.mcast_miss.encode_key) < 16)
433 fatal("mcast_encode_key is too short, must be 16 characters");
e66d7923 434 }
62e76326 435
62ee09ca 436#endif
437}
438
21f6708d 439#if USE_FORW_VIA_DB
1afe05c5 440
62ee09ca 441static void
caf0eda4 442fvdbRegisterWithCacheManager(void)
62ee09ca 443{
8822ebee
AR
444 Mgr::RegisterAction("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
445 Mgr::RegisterAction("forw_headers", "X-Forwarded-For Request Headers",
9386f99d 446 fvdbDumpForwarded, 0, 1);
d21f1c54 447}
448
449void
cf1f23ee 450fvdbCountVia(const SBuf &headerValue)
d21f1c54 451{
cf1f23ee 452 ++TheViaCounts[headerValue];
d21f1c54 453}
454
455void
9386f99d 456fvdbCountForwarded(const SBuf &key)
d21f1c54 457{
9386f99d 458 ++TheForwardedCounts[key];
d21f1c54 459}
460
cf1f23ee
FC
461static void
462fvdbDumpCounts(StoreEntry &e, const HeaderValueCounts &counts)
463{
464 PackableStream os(e);
465 for (const auto &i : counts)
466 os << std::setw(9) << i.second << ' ' << i.first << "\n";
467}
468
d21f1c54 469static void
9bea1d5b 470fvdbDumpVia(StoreEntry * e)
d21f1c54 471{
cf1f23ee
FC
472 assert(e);
473 fvdbDumpCounts(*e, TheViaCounts);
d21f1c54 474}
1afe05c5 475
d21f1c54 476static void
9386f99d 477fvdbDumpForwarded(StoreEntry * e)
d21f1c54 478{
9386f99d
FC
479 assert(e);
480 fvdbDumpCounts(*e, TheForwardedCounts);
d21f1c54 481}
482
483static void
9bea1d5b 484fvdbClear(void)
d21f1c54 485{
cf1f23ee 486 TheViaCounts.clear();
9386f99d 487 TheForwardedCounts.clear();
d21f1c54 488}
489
1afe05c5 490#endif
e66d7923 491
492#if MULTICAST_MISS_STREAM
493/*
494 * From http://www.io.com/~paulhart/game/algorithms/tea.html
495 *
496 * size of 'ibuf' must be a multiple of 2.
497 * size of 'key' must be 4.
498 * 'ibuf' is modified in place, encrypted data is written in
499 * network byte order.
500 */
501static void
9bea1d5b 502mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
e66d7923 503{
9bea1d5b 504 unsigned int y;
505 unsigned int z;
506 unsigned int sum;
507 const unsigned int delta = 0x9e3779b9;
508 unsigned int n = 32;
509 const unsigned int k0 = htonl(key[0]);
510 const unsigned int k1 = htonl(key[1]);
511 const unsigned int k2 = htonl(key[2]);
512 const unsigned int k3 = htonl(key[3]);
513 int i;
62e76326 514
9bea1d5b 515 for (i = 0; i < isize; i += 2) {
62e76326 516 y = htonl(ibuf[i]);
517 z = htonl(ibuf[i + 1]);
518 sum = 0;
519
5e263176 520 for (n = 32; n; --n) {
62e76326 521 sum += delta;
522 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
523 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
524 }
525
526 ibuf[i] = htonl(y);
527 ibuf[i + 1] = htonl(z);
e66d7923 528 }
529}
530
531#endif
c3609322 532
533#if HEADERS_LOG
534void
60745f24 535headersLog(int cs, int pq, const HttpRequestMethod& method, void *data)
c3609322 536{
9bea1d5b 537 HttpReply *rep;
190154cf 538 HttpRequest *req;
9bea1d5b 539 unsigned short magic = 0;
540 unsigned char M = (unsigned char) m;
9bea1d5b 541 char *hmask;
542 int ccmask = 0;
62e76326 543
9bea1d5b 544 if (0 == pq) {
62e76326 545 /* reply */
546 rep = data;
547 req = NULL;
548 magic = 0x0050;
549 hmask = rep->header.mask;
550
551 if (rep->cache_control)
552 ccmask = rep->cache_control->mask;
9bea1d5b 553 } else {
62e76326 554 /* request */
555 req = data;
556 rep = NULL;
557 magic = 0x0051;
558 hmask = req->header.mask;
559
560 if (req->cache_control)
561 ccmask = req->cache_control->mask;
c3609322 562 }
62e76326 563
9bea1d5b 564 if (0 == cs) {
62e76326 565 /* client */
566 magic |= 0x4300;
9bea1d5b 567 } else {
62e76326 568 /* server */
569 magic |= 0x5300;
c3609322 570 }
62e76326 571
9bea1d5b 572 magic = htons(magic);
573 ccmask = htonl(ccmask);
62e76326 574
9b769c67 575 unsigned short S = 0;
9bea1d5b 576 if (0 == pq)
9b769c67 577 S = static_cast<unsigned short>(rep->sline.status());
62e76326 578
9bea1d5b 579 logfileWrite(headerslog, &magic, sizeof(magic));
580 logfileWrite(headerslog, &M, sizeof(M));
581 logfileWrite(headerslog, &S, sizeof(S));
582 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
583 logfileWrite(headerslog, &ccmask, sizeof(int));
c3609322 584}
585
586#endif
f53969cc 587