2 #include "AccessLogEntry.h"
3 #include "comm/Connection.h"
4 #include "err_detail_type.h"
6 #include "format/Format.h"
7 #include "format/Quoting.h"
8 #include "format/Tokens.h"
9 #include "HttpRequest.h"
12 #include "SquidTime.h"
16 Format::Format::Format(const char *n
) :
23 Format::Format::~Format()
25 // erase the list without consuming stack space
27 // unlink the next entry for deletion
40 Format::Format::parse(char *def
)
43 Token
*new_lt
, *last_lt
;
44 enum Quoting quote
= LOG_QUOTE_NONE
;
46 debugs(46, 2, HERE
<< "got definition '" << def
<< "'");
49 debugs(46, DBG_IMPORTANT
, "WARNING: existing format for '" << name
<< " " << def
<< "'");
53 /* very inefficent parser, but who cares, this needs to be simple */
54 /* First off, let's tokenize, we'll optimize in a second pass.
55 * A token can either be a %-prefixed sequence (usually a dynamic
56 * token but it can be an escaped sequence), or a string. */
58 eos
= def
+ strlen(def
);
59 format
= new_lt
= last_lt
= new Token
;
60 cur
+= new_lt
->parse(cur
, "e
);
64 last_lt
->next
= new_lt
;
66 cur
+= new_lt
->parse(cur
, "e
);
73 Format::Format::dump(StoreEntry
* entry
, const char *name
)
77 // loop rather than recursing to conserve stack space.
78 for (Format
*format
= this; format
; format
= format
->next
) {
79 debugs(46, 3, HERE
<< "Dumping format definition for " << format
->name
);
80 storeAppendPrintf(entry
, "format %s ", format
->name
);
82 for (Token
*t
= format
->format
; t
; t
= t
->next
) {
83 if (t
->type
== LFT_STRING
)
84 storeAppendPrintf(entry
, "%s", t
->data
.string
);
88 ByteCode_t type
= t
->type
;
96 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
99 case LFT_ICAP_REQ_HEADER_ELEM
:
100 case LFT_ICAP_REP_HEADER_ELEM
:
102 case LFT_REQUEST_HEADER_ELEM
:
103 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
104 case LFT_REPLY_HEADER_ELEM
:
106 if (t
->data
.header
.separator
!= ',')
107 snprintf(argbuf
, sizeof(argbuf
), "%s:%c%s", t
->data
.header
.header
, t
->data
.header
.separator
, t
->data
.header
.element
);
109 snprintf(argbuf
, sizeof(argbuf
), "%s:%s", t
->data
.header
.header
, t
->data
.header
.element
);
114 case LFT_REQUEST_HEADER_ELEM
:
115 type
= LFT_REQUEST_HEADER_ELEM
; // XXX: remove _ELEM?
117 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
118 type
= LFT_ADAPTED_REQUEST_HEADER_ELEM
; // XXX: remove _ELEM?
120 case LFT_REPLY_HEADER_ELEM
:
121 type
= LFT_REPLY_HEADER_ELEM
; // XXX: remove _ELEM?
124 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
125 type
= LFT_ADAPTATION_LAST_HEADER
;
129 case LFT_ICAP_REQ_HEADER_ELEM
:
130 type
= LFT_ICAP_REQ_HEADER
;
132 case LFT_ICAP_REP_HEADER_ELEM
:
133 type
= LFT_ICAP_REP_HEADER
;
142 case LFT_REQUEST_ALL_HEADERS
:
143 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
144 case LFT_REPLY_ALL_HEADERS
:
147 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
150 case LFT_ICAP_REQ_ALL_HEADERS
:
151 case LFT_ICAP_REP_ALL_HEADERS
:
155 case LFT_REQUEST_ALL_HEADERS
:
156 type
= LFT_REQUEST_HEADER
;
158 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
159 type
= LFT_ADAPTED_REQUEST_HEADER
;
161 case LFT_REPLY_ALL_HEADERS
:
162 type
= LFT_REPLY_HEADER
;
165 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
166 type
= LFT_ADAPTATION_LAST_HEADER
;
170 case LFT_ICAP_REQ_ALL_HEADERS
:
171 type
= LFT_ICAP_REQ_HEADER
;
173 case LFT_ICAP_REP_ALL_HEADERS
:
174 type
= LFT_ICAP_REP_HEADER
;
185 arg
= t
->data
.string
;
190 entry
->append("%", 1);
194 case LOG_QUOTE_QUOTES
:
195 entry
->append("\"", 1);
198 case LOG_QUOTE_MIMEBLOB
:
199 entry
->append("[", 1);
203 entry
->append("#", 1);
207 entry
->append("'", 1);
215 entry
->append("-", 1);
218 entry
->append("0", 1);
221 storeAppendPrintf(entry
, "%d", (int) t
->width
);
224 storeAppendPrintf(entry
, ".%d", (int) t
->precision
);
227 storeAppendPrintf(entry
, "{%s}", arg
);
229 storeAppendPrintf(entry
, "%s", t
->label
);
232 entry
->append(" ", 1);
236 entry
->append("\n", 1);
242 log_quoted_string(const char *str
, char *out
)
247 int l
= strcspn(str
, "\"\\\r\n\t");
287 Format::Format::assemble(MemBuf
&mb
, AccessLogEntry
*al
, int logSequenceNumber
) const
292 for (Token
*fmt
= format
; fmt
!= NULL
; fmt
= fmt
->next
) { /* for each token */
293 const char *out
= NULL
;
308 out
= fmt
->data
.string
;
311 case LFT_CLIENT_IP_ADDRESS
:
312 if (al
->cache
.caddr
.IsNoAddr()) // e.g., ICAP OPTIONS lack client
315 out
= al
->cache
.caddr
.NtoA(tmp
,1024);
318 case LFT_CLIENT_FQDN
:
319 if (al
->cache
.caddr
.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
322 out
= fqdncache_gethostbyaddr(al
->cache
.caddr
, FQDN_LOOKUP_IF_MISS
);
324 out
= al
->cache
.caddr
.NtoA(tmp
,1024);
329 case LFT_CLIENT_PORT
:
331 outint
= al
->request
->client_addr
.GetPort();
338 // TODO make the ACL checklist have a direct link to any TCP details.
339 if (al
->request
&& al
->request
->clientConnectionManager
.valid() && al
->request
->clientConnectionManager
->clientConnection
!= NULL
) {
340 if (al
->request
->clientConnectionManager
->clientConnection
->remote
.IsIPv4())
341 al
->request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(tmp
, 1024);
343 al
->request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(tmp
, 1024);
351 case LFT_SERVER_IP_ADDRESS
:
352 if (al
->hier
.tcpServer
!= NULL
) {
353 out
= al
->hier
.tcpServer
->remote
.NtoA(tmp
,sizeof(tmp
));
357 case LFT_SERVER_FQDN_OR_PEER_NAME
:
361 case LFT_SERVER_PORT
:
362 if (al
->hier
.tcpServer
!= NULL
) {
363 outint
= al
->hier
.tcpServer
->remote
.GetPort();
368 case LFT_CLIENT_LOCAL_IP_OLD_31
:
369 case LFT_CLIENT_LOCAL_IP
:
370 if (al
->tcpClient
!= NULL
) {
371 out
= al
->tcpClient
->local
.NtoA(tmp
,sizeof(tmp
));
375 case LFT_CLIENT_LOCAL_PORT_OLD_31
:
376 case LFT_CLIENT_LOCAL_PORT
:
377 if (al
->tcpClient
!= NULL
) {
378 outint
= al
->tcpClient
->local
.GetPort();
383 case LFT_SERVER_LOCAL_IP_OLD_27
:
384 case LFT_SERVER_LOCAL_IP
:
385 if (al
->hier
.tcpServer
!= NULL
) {
386 out
= al
->hier
.tcpServer
->local
.NtoA(tmp
,sizeof(tmp
));
390 case LFT_SERVER_LOCAL_PORT
:
391 if (al
->hier
.tcpServer
!= NULL
) {
392 outint
= al
->hier
.tcpServer
->local
.GetPort();
398 case LFT_TIME_SECONDS_SINCE_EPOCH
:
399 // some platforms store time in 32-bit, some 64-bit...
400 outoff
= static_cast<int64_t>(current_time
.tv_sec
);
404 case LFT_TIME_SUBSECOND
:
405 outint
= current_time
.tv_usec
/ fmt
->divisor
;
410 case LFT_TIME_LOCALTIME
:
416 spec
= fmt
->data
.timespec
;
418 if (fmt
->type
== LFT_TIME_LOCALTIME
) {
420 spec
= "%d/%b/%Y:%H:%M:%S %z";
421 t
= localtime(&squid_curtime
);
424 spec
= "%d/%b/%Y:%H:%M:%S";
426 t
= gmtime(&squid_curtime
);
429 strftime(tmp
, sizeof(tmp
), spec
, t
);
436 case LFT_TIME_TO_HANDLE_REQUEST
:
437 outint
= al
->cache
.msec
;
441 case LFT_PEER_RESPONSE_TIME
:
442 if (al
->hier
.peer_response_time
< 0) {
445 outoff
= al
->hier
.peer_response_time
;
450 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME
:
451 if (al
->hier
.total_response_time
< 0) {
454 outoff
= al
->hier
.total_response_time
;
459 case LFT_DNS_WAIT_TIME
:
460 if (al
->request
&& al
->request
->dnsWait
>= 0) {
461 outint
= al
->request
->dnsWait
;
466 case LFT_REQUEST_HEADER
:
469 sb
= al
->request
->header
.getByName(fmt
->data
.header
.header
);
471 out
= sb
.termedBuf();
477 case LFT_ADAPTED_REQUEST_HEADER
:
480 sb
= al
->adapted_request
->header
.getByName(fmt
->data
.header
.header
);
482 out
= sb
.termedBuf();
488 case LFT_REPLY_HEADER
:
490 sb
= al
->reply
->header
.getByName(fmt
->data
.header
.header
);
492 out
= sb
.termedBuf();
499 case LTF_ADAPTATION_SUM_XACT_TIMES
:
501 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
503 ah
->sumLogString(fmt
->data
.string
, sb
);
504 out
= sb
.termedBuf();
508 case LTF_ADAPTATION_ALL_XACT_TIMES
:
510 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
512 ah
->allLogString(fmt
->data
.string
, sb
);
513 out
= sb
.termedBuf();
517 case LFT_ADAPTATION_LAST_HEADER
:
519 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
520 if (ah
!= NULL
) // XXX: add adapt::<all_h but use lastMeta here
521 sb
= ah
->allMeta
.getByName(fmt
->data
.header
.header
);
524 // XXX: here and elsewhere: move such code inside the if guard
525 out
= sb
.termedBuf();
531 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
533 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
534 if (ah
!= NULL
) // XXX: add adapt::<all_h but use lastMeta here
535 sb
= ah
->allMeta
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
538 out
= sb
.termedBuf();
544 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
545 out
= al
->adapt
.last_meta
;
555 out
= al
->icap
.hostAddr
.NtoA(tmp
,1024);
558 case LFT_ICAP_SERV_NAME
:
559 out
= al
->icap
.serviceName
.termedBuf();
562 case LFT_ICAP_REQUEST_URI
:
563 out
= al
->icap
.reqUri
.termedBuf();
566 case LFT_ICAP_REQUEST_METHOD
:
567 out
= Adaptation::Icap::ICAP::methodStr(al
->icap
.reqMethod
);
570 case LFT_ICAP_BYTES_SENT
:
571 outoff
= al
->icap
.bytesSent
;
575 case LFT_ICAP_BYTES_READ
:
576 outoff
= al
->icap
.bytesRead
;
580 case LFT_ICAP_BODY_BYTES_READ
:
581 if (al
->icap
.bodyBytesRead
>= 0) {
582 outoff
= al
->icap
.bodyBytesRead
;
585 // else if icap.bodyBytesRead < 0, we do not have any http data,
586 // so just print a "-" (204 responses etc)
589 case LFT_ICAP_REQ_HEADER
:
590 if (NULL
!= al
->icap
.request
) {
591 sb
= al
->icap
.request
->header
.getByName(fmt
->data
.header
.header
);
592 out
= sb
.termedBuf();
597 case LFT_ICAP_REQ_HEADER_ELEM
:
599 sb
= al
->icap
.request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
601 out
= sb
.termedBuf();
607 case LFT_ICAP_REQ_ALL_HEADERS
:
608 if (al
->icap
.request
) {
609 HttpHeaderPos pos
= HttpHeaderInitPos
;
610 while (const HttpHeaderEntry
*e
= al
->icap
.request
->header
.getEntry(&pos
)) {
616 out
= sb
.termedBuf();
621 case LFT_ICAP_REP_HEADER
:
622 if (NULL
!= al
->icap
.reply
) {
623 sb
= al
->icap
.reply
->header
.getByName(fmt
->data
.header
.header
);
624 out
= sb
.termedBuf();
629 case LFT_ICAP_REP_HEADER_ELEM
:
630 if (NULL
!= al
->icap
.reply
)
631 sb
= al
->icap
.reply
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
633 out
= sb
.termedBuf();
639 case LFT_ICAP_REP_ALL_HEADERS
:
640 if (al
->icap
.reply
) {
641 HttpHeaderPos pos
= HttpHeaderInitPos
;
642 while (const HttpHeaderEntry
*e
= al
->icap
.reply
->header
.getEntry(&pos
)) {
648 out
= sb
.termedBuf();
653 case LFT_ICAP_TR_RESPONSE_TIME
:
654 outint
= al
->icap
.trTime
;
658 case LFT_ICAP_IO_TIME
:
659 outint
= al
->icap
.ioTime
;
663 case LFT_ICAP_STATUS_CODE
:
664 outint
= al
->icap
.resStatus
;
668 case LFT_ICAP_OUTCOME
:
669 out
= al
->icap
.outcome
;
672 case LFT_ICAP_TOTAL_TIME
:
673 outint
= al
->icap
.processingTime
;
677 case LFT_REQUEST_HEADER_ELEM
:
679 sb
= al
->request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
681 out
= sb
.termedBuf();
687 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
688 if (al
->adapted_request
)
689 sb
= al
->adapted_request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
691 out
= sb
.termedBuf();
697 case LFT_REPLY_HEADER_ELEM
:
699 sb
= al
->reply
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
701 out
= sb
.termedBuf();
707 case LFT_REQUEST_ALL_HEADERS
:
708 out
= al
->headers
.request
;
714 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
715 out
= al
->headers
.adapted_request
;
721 case LFT_REPLY_ALL_HEADERS
:
722 out
= al
->headers
.reply
;
729 out
= QuoteUrlEncodeUsername(al
->cache
.authuser
);
732 out
= QuoteUrlEncodeUsername(al
->cache
.extuser
);
737 out
= QuoteUrlEncodeUsername(al
->cache
.ssluser
);
742 out
= QuoteUrlEncodeUsername(al
->cache
.rfc931
);
749 out
= QuoteUrlEncodeUsername(al
->cache
.authuser
);
756 out
= QuoteUrlEncodeUsername(al
->cache
.rfc931
);
762 case LFT_USER_EXTERNAL
:
763 out
= QuoteUrlEncodeUsername(al
->cache
.extuser
);
769 /* case LFT_USER_REALM: */
770 /* case LFT_USER_SCHEME: */
772 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
773 // but compiler complains if ommited
774 case LFT_HTTP_SENT_STATUS_CODE_OLD_30
:
775 case LFT_HTTP_SENT_STATUS_CODE
:
776 outint
= al
->http
.code
;
782 case LFT_HTTP_RECEIVED_STATUS_CODE
:
783 if (al
->hier
.peer_reply_status
== HTTP_STATUS_NONE
) {
786 outint
= al
->hier
.peer_reply_status
;
790 /* case LFT_HTTP_STATUS:
791 * out = statusline->text;
795 case LFT_HTTP_BODY_BYTES_READ
:
796 if (al
->hier
.bodyBytesRead
>= 0) {
797 outoff
= al
->hier
.bodyBytesRead
;
800 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
801 // a peer server so just print a "-" (eg requests served from cache,
802 // or internal error messages).
805 case LFT_SQUID_STATUS
:
806 if (al
->http
.timedout
|| al
->http
.aborted
) {
807 snprintf(tmp
, sizeof(tmp
), "%s%s", log_tags
[al
->cache
.code
],
808 al
->http
.statusSfx());
811 out
= log_tags
[al
->cache
.code
];
816 case LFT_SQUID_ERROR
:
817 if (al
->request
&& al
->request
->errType
!= ERR_NONE
)
818 out
= errorPageName(al
->request
->errType
);
821 case LFT_SQUID_ERROR_DETAIL
:
822 if (al
->request
&& al
->request
->errDetail
!= ERR_DETAIL_NONE
) {
823 if (al
->request
->errDetail
> ERR_DETAIL_START
&&
824 al
->request
->errDetail
< ERR_DETAIL_MAX
)
825 out
= errorDetailName(al
->request
->errDetail
);
827 if (al
->request
->errDetail
>= ERR_DETAIL_EXCEPTION_START
)
828 snprintf(tmp
, sizeof(tmp
), "%s=0x%X",
829 errorDetailName(al
->request
->errDetail
), (uint32_t) al
->request
->errDetail
);
831 snprintf(tmp
, sizeof(tmp
), "%s=%d",
832 errorDetailName(al
->request
->errDetail
), al
->request
->errDetail
);
838 case LFT_SQUID_HIERARCHY
:
839 if (al
->hier
.ping
.timedout
)
840 mb
.append("TIMEOUT_", 8);
842 out
= hier_code_str
[al
->hier
.code
];
847 out
= al
->http
.content_type
;
851 case LFT_CLIENT_REQ_METHOD
:
853 out
= al
->request
->method
.image();
858 case LFT_CLIENT_REQ_URI
:
859 // original client URI
861 out
= urlCanonical(al
->request
);
866 case LFT_REQUEST_URLPATH_OLD_31
:
867 case LFT_CLIENT_REQ_URLPATH
:
869 out
= al
->request
->urlpath
.termedBuf();
874 case LFT_CLIENT_REQ_VERSION
:
876 snprintf(tmp
, sizeof(tmp
), "%d.%d", (int) al
->request
->http_ver
.major
, (int) al
->request
->http_ver
.minor
);
881 case LFT_REQUEST_METHOD
:
882 out
= al
->_private
.method_str
;
885 case LFT_REQUEST_URI
:
889 case LFT_REQUEST_VERSION_OLD_2X
:
890 case LFT_REQUEST_VERSION
:
891 snprintf(tmp
, sizeof(tmp
), "%d.%d", (int) al
->http
.version
.major
, (int) al
->http
.version
.minor
);
895 case LFT_SERVER_REQ_METHOD
:
896 if (al
->adapted_request
) {
897 out
= al
->adapted_request
->method
.image();
902 case LFT_SERVER_REQ_URI
:
903 // adapted request URI sent to server/peer
904 if (al
->adapted_request
) {
905 out
= urlCanonical(al
->adapted_request
);
910 case LFT_SERVER_REQ_URLPATH
:
911 if (al
->adapted_request
) {
912 out
= al
->adapted_request
->urlpath
.termedBuf();
917 case LFT_SERVER_REQ_VERSION
:
918 if (al
->adapted_request
) {
919 snprintf(tmp
, sizeof(tmp
), "%d.%d",
920 (int) al
->adapted_request
->http_ver
.major
,
921 (int) al
->adapted_request
->http_ver
.minor
);
926 case LFT_REQUEST_SIZE_TOTAL
:
927 outoff
= al
->cache
.requestSize
;
931 /*case LFT_REQUEST_SIZE_LINE: */
932 case LFT_REQUEST_SIZE_HEADERS
:
933 outoff
= al
->cache
.requestHeadersSize
;
936 /*case LFT_REQUEST_SIZE_BODY: */
937 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
939 case LFT_REPLY_SIZE_TOTAL
:
940 outoff
= al
->cache
.replySize
;
944 case LFT_REPLY_HIGHOFFSET
:
945 outoff
= al
->cache
.highOffset
;
951 case LFT_REPLY_OBJECTSIZE
:
952 outoff
= al
->cache
.objectSize
;
958 /*case LFT_REPLY_SIZE_LINE: */
959 case LFT_REPLY_SIZE_HEADERS
:
960 outint
= al
->cache
.replyHeadersSize
;
963 /*case LFT_REPLY_SIZE_BODY: */
964 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
968 out
= al
->request
->tag
.termedBuf();
974 case LFT_IO_SIZE_TOTAL
:
975 outint
= al
->cache
.requestSize
+ al
->cache
.replySize
;
981 out
= al
->request
->extacl_log
.termedBuf();
987 case LFT_SEQUENCE_NUMBER
:
988 outoff
= logSequenceNumber
;
999 snprintf(tmp
, sizeof(tmp
), "%0*" PRId64
, fmt
->zero
? (int) fmt
->width
: 0, outoff
);
1003 snprintf(tmp
, sizeof(tmp
), "%0*ld", fmt
->zero
? (int) fmt
->width
: 0, outint
);
1008 if (quote
|| fmt
->quote
!= LOG_QUOTE_NONE
) {
1009 char *newout
= NULL
;
1012 switch (fmt
->quote
) {
1014 case LOG_QUOTE_NONE
:
1015 newout
= rfc1738_escape_unescaped(out
);
1018 case LOG_QUOTE_QUOTES
: {
1019 size_t out_len
= static_cast<size_t>(strlen(out
)) * 2 + 1;
1020 if (out_len
>= sizeof(tmp
)) {
1021 newout
= (char *)xmalloc(out_len
);
1025 log_quoted_string(out
, newout
);
1029 case LOG_QUOTE_MIMEBLOB
:
1030 newout
= QuoteMimeBlob(out
);
1035 newout
= rfc1738_escape(out
);
1054 mb
.Printf("%-*s", (int) fmt
->width
, out
);
1056 mb
.Printf("%*s", (int) fmt
->width
, out
);
1058 mb
.append(out
, strlen(out
));