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 for (struct TokenTableEntry
*te
= TokenTable
; te
->config
!= NULL
; te
++) {
230 if (te
->token_type
== type
) {
231 storeAppendPrintf(entry
, "%s", te
->config
);
237 entry
->append(" ", 1);
241 entry
->append("\n", 1);
247 log_quoted_string(const char *str
, char *out
)
252 int l
= strcspn(str
, "\"\\\r\n\t");
292 Format::Format::assemble(MemBuf
&mb
, AccessLogEntry
*al
, int logSequenceNumber
) const
297 for (Token
*fmt
= format
; fmt
!= NULL
; fmt
= fmt
->next
) { /* for each token */
298 const char *out
= NULL
;
313 out
= fmt
->data
.string
;
316 case LFT_CLIENT_IP_ADDRESS
:
317 if (al
->cache
.caddr
.IsNoAddr()) // e.g., ICAP OPTIONS lack client
320 out
= al
->cache
.caddr
.NtoA(tmp
,1024);
323 case LFT_CLIENT_FQDN
:
324 if (al
->cache
.caddr
.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
327 out
= fqdncache_gethostbyaddr(al
->cache
.caddr
, FQDN_LOOKUP_IF_MISS
);
329 out
= al
->cache
.caddr
.NtoA(tmp
,1024);
334 case LFT_CLIENT_PORT
:
336 outint
= al
->request
->client_addr
.GetPort();
343 // TODO make the ACL checklist have a direct link to any TCP details.
344 if (al
->request
&& al
->request
->clientConnectionManager
.valid() && al
->request
->clientConnectionManager
->clientConnection
!= NULL
) {
345 if (al
->request
->clientConnectionManager
->clientConnection
->remote
.IsIPv4())
346 al
->request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(tmp
, 1024);
348 al
->request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(tmp
, 1024);
356 case LFT_SERVER_IP_ADDRESS
:
357 if (al
->hier
.tcpServer
!= NULL
) {
358 out
= al
->hier
.tcpServer
->remote
.NtoA(tmp
,sizeof(tmp
));
362 case LFT_SERVER_FQDN_OR_PEER_NAME
:
366 case LFT_SERVER_PORT
:
367 if (al
->hier
.tcpServer
!= NULL
) {
368 outint
= al
->hier
.tcpServer
->remote
.GetPort();
373 case LFT_CLIENT_LOCAL_IP_OLD_31
:
374 case LFT_CLIENT_LOCAL_IP
:
375 if (al
->tcpClient
!= NULL
) {
376 out
= al
->tcpClient
->local
.NtoA(tmp
,sizeof(tmp
));
380 case LFT_CLIENT_LOCAL_PORT_OLD_31
:
381 case LFT_CLIENT_LOCAL_PORT
:
382 if (al
->tcpClient
!= NULL
) {
383 outint
= al
->tcpClient
->local
.GetPort();
388 case LFT_SERVER_LOCAL_IP_OLD_27
:
389 case LFT_SERVER_LOCAL_IP
:
390 if (al
->hier
.tcpServer
!= NULL
) {
391 out
= al
->hier
.tcpServer
->local
.NtoA(tmp
,sizeof(tmp
));
395 case LFT_SERVER_LOCAL_PORT
:
396 if (al
->hier
.tcpServer
!= NULL
) {
397 outint
= al
->hier
.tcpServer
->local
.GetPort();
403 case LFT_TIME_SECONDS_SINCE_EPOCH
:
404 // some platforms store time in 32-bit, some 64-bit...
405 outoff
= static_cast<int64_t>(current_time
.tv_sec
);
409 case LFT_TIME_SUBSECOND
:
410 outint
= current_time
.tv_usec
/ fmt
->divisor
;
415 case LFT_TIME_LOCALTIME
:
421 spec
= fmt
->data
.timespec
;
423 if (fmt
->type
== LFT_TIME_LOCALTIME
) {
425 spec
= "%d/%b/%Y:%H:%M:%S %z";
426 t
= localtime(&squid_curtime
);
429 spec
= "%d/%b/%Y:%H:%M:%S";
431 t
= gmtime(&squid_curtime
);
434 strftime(tmp
, sizeof(tmp
), spec
, t
);
441 case LFT_TIME_TO_HANDLE_REQUEST
:
442 outint
= al
->cache
.msec
;
446 case LFT_PEER_RESPONSE_TIME
:
447 if (al
->hier
.peer_response_time
< 0) {
450 outoff
= al
->hier
.peer_response_time
;
455 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME
:
456 if (al
->hier
.total_response_time
< 0) {
459 outoff
= al
->hier
.total_response_time
;
464 case LFT_DNS_WAIT_TIME
:
465 if (al
->request
&& al
->request
->dnsWait
>= 0) {
466 outint
= al
->request
->dnsWait
;
471 case LFT_REQUEST_HEADER
:
474 sb
= al
->request
->header
.getByName(fmt
->data
.header
.header
);
476 out
= sb
.termedBuf();
482 case LFT_ADAPTED_REQUEST_HEADER
:
485 sb
= al
->adapted_request
->header
.getByName(fmt
->data
.header
.header
);
487 out
= sb
.termedBuf();
493 case LFT_REPLY_HEADER
:
495 sb
= al
->reply
->header
.getByName(fmt
->data
.header
.header
);
497 out
= sb
.termedBuf();
504 case LTF_ADAPTATION_SUM_XACT_TIMES
:
506 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
508 ah
->sumLogString(fmt
->data
.string
, sb
);
509 out
= sb
.termedBuf();
513 case LTF_ADAPTATION_ALL_XACT_TIMES
:
515 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
517 ah
->allLogString(fmt
->data
.string
, sb
);
518 out
= sb
.termedBuf();
522 case LFT_ADAPTATION_LAST_HEADER
:
524 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
525 if (ah
!= NULL
) // XXX: add adapt::<all_h but use lastMeta here
526 sb
= ah
->allMeta
.getByName(fmt
->data
.header
.header
);
529 // XXX: here and elsewhere: move such code inside the if guard
530 out
= sb
.termedBuf();
536 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
538 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
539 if (ah
!= NULL
) // XXX: add adapt::<all_h but use lastMeta here
540 sb
= ah
->allMeta
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
543 out
= sb
.termedBuf();
549 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
550 out
= al
->adapt
.last_meta
;
560 out
= al
->icap
.hostAddr
.NtoA(tmp
,1024);
563 case LFT_ICAP_SERV_NAME
:
564 out
= al
->icap
.serviceName
.termedBuf();
567 case LFT_ICAP_REQUEST_URI
:
568 out
= al
->icap
.reqUri
.termedBuf();
571 case LFT_ICAP_REQUEST_METHOD
:
572 out
= Adaptation::Icap::ICAP::methodStr(al
->icap
.reqMethod
);
575 case LFT_ICAP_BYTES_SENT
:
576 outoff
= al
->icap
.bytesSent
;
580 case LFT_ICAP_BYTES_READ
:
581 outoff
= al
->icap
.bytesRead
;
585 case LFT_ICAP_BODY_BYTES_READ
:
586 if (al
->icap
.bodyBytesRead
>= 0) {
587 outoff
= al
->icap
.bodyBytesRead
;
590 // else if icap.bodyBytesRead < 0, we do not have any http data,
591 // so just print a "-" (204 responses etc)
594 case LFT_ICAP_REQ_HEADER
:
595 if (NULL
!= al
->icap
.request
) {
596 sb
= al
->icap
.request
->header
.getByName(fmt
->data
.header
.header
);
597 out
= sb
.termedBuf();
602 case LFT_ICAP_REQ_HEADER_ELEM
:
604 sb
= al
->icap
.request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
606 out
= sb
.termedBuf();
612 case LFT_ICAP_REQ_ALL_HEADERS
:
613 if (al
->icap
.request
) {
614 HttpHeaderPos pos
= HttpHeaderInitPos
;
615 while (const HttpHeaderEntry
*e
= al
->icap
.request
->header
.getEntry(&pos
)) {
621 out
= sb
.termedBuf();
626 case LFT_ICAP_REP_HEADER
:
627 if (NULL
!= al
->icap
.reply
) {
628 sb
= al
->icap
.reply
->header
.getByName(fmt
->data
.header
.header
);
629 out
= sb
.termedBuf();
634 case LFT_ICAP_REP_HEADER_ELEM
:
635 if (NULL
!= al
->icap
.reply
)
636 sb
= al
->icap
.reply
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
638 out
= sb
.termedBuf();
644 case LFT_ICAP_REP_ALL_HEADERS
:
645 if (al
->icap
.reply
) {
646 HttpHeaderPos pos
= HttpHeaderInitPos
;
647 while (const HttpHeaderEntry
*e
= al
->icap
.reply
->header
.getEntry(&pos
)) {
653 out
= sb
.termedBuf();
658 case LFT_ICAP_TR_RESPONSE_TIME
:
659 outint
= al
->icap
.trTime
;
663 case LFT_ICAP_IO_TIME
:
664 outint
= al
->icap
.ioTime
;
668 case LFT_ICAP_STATUS_CODE
:
669 outint
= al
->icap
.resStatus
;
673 case LFT_ICAP_OUTCOME
:
674 out
= al
->icap
.outcome
;
677 case LFT_ICAP_TOTAL_TIME
:
678 outint
= al
->icap
.processingTime
;
682 case LFT_REQUEST_HEADER_ELEM
:
684 sb
= al
->request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
686 out
= sb
.termedBuf();
692 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
693 if (al
->adapted_request
)
694 sb
= al
->adapted_request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
696 out
= sb
.termedBuf();
702 case LFT_REPLY_HEADER_ELEM
:
704 sb
= al
->reply
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
706 out
= sb
.termedBuf();
712 case LFT_REQUEST_ALL_HEADERS
:
713 out
= al
->headers
.request
;
719 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
720 out
= al
->headers
.adapted_request
;
726 case LFT_REPLY_ALL_HEADERS
:
727 out
= al
->headers
.reply
;
734 out
= QuoteUrlEncodeUsername(al
->cache
.authuser
);
737 out
= QuoteUrlEncodeUsername(al
->cache
.extuser
);
742 out
= QuoteUrlEncodeUsername(al
->cache
.ssluser
);
747 out
= QuoteUrlEncodeUsername(al
->cache
.rfc931
);
754 out
= QuoteUrlEncodeUsername(al
->cache
.authuser
);
761 out
= QuoteUrlEncodeUsername(al
->cache
.rfc931
);
767 case LFT_USER_EXTERNAL
:
768 out
= QuoteUrlEncodeUsername(al
->cache
.extuser
);
774 /* case LFT_USER_REALM: */
775 /* case LFT_USER_SCHEME: */
777 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
778 // but compiler complains if ommited
779 case LFT_HTTP_SENT_STATUS_CODE_OLD_30
:
780 case LFT_HTTP_SENT_STATUS_CODE
:
781 outint
= al
->http
.code
;
787 case LFT_HTTP_RECEIVED_STATUS_CODE
:
788 if (al
->hier
.peer_reply_status
== HTTP_STATUS_NONE
) {
791 outint
= al
->hier
.peer_reply_status
;
795 /* case LFT_HTTP_STATUS:
796 * out = statusline->text;
800 case LFT_HTTP_BODY_BYTES_READ
:
801 if (al
->hier
.bodyBytesRead
>= 0) {
802 outoff
= al
->hier
.bodyBytesRead
;
805 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
806 // a peer server so just print a "-" (eg requests served from cache,
807 // or internal error messages).
810 case LFT_SQUID_STATUS
:
811 if (al
->http
.timedout
|| al
->http
.aborted
) {
812 snprintf(tmp
, sizeof(tmp
), "%s%s", log_tags
[al
->cache
.code
],
813 al
->http
.statusSfx());
816 out
= log_tags
[al
->cache
.code
];
821 case LFT_SQUID_ERROR
:
822 if (al
->request
&& al
->request
->errType
!= ERR_NONE
)
823 out
= errorPageName(al
->request
->errType
);
826 case LFT_SQUID_ERROR_DETAIL
:
827 if (al
->request
&& al
->request
->errDetail
!= ERR_DETAIL_NONE
) {
828 if (al
->request
->errDetail
> ERR_DETAIL_START
&&
829 al
->request
->errDetail
< ERR_DETAIL_MAX
)
830 out
= errorDetailName(al
->request
->errDetail
);
832 if (al
->request
->errDetail
>= ERR_DETAIL_EXCEPTION_START
)
833 snprintf(tmp
, sizeof(tmp
), "%s=0x%X",
834 errorDetailName(al
->request
->errDetail
), (uint32_t) al
->request
->errDetail
);
836 snprintf(tmp
, sizeof(tmp
), "%s=%d",
837 errorDetailName(al
->request
->errDetail
), al
->request
->errDetail
);
843 case LFT_SQUID_HIERARCHY
:
844 if (al
->hier
.ping
.timedout
)
845 mb
.append("TIMEOUT_", 8);
847 out
= hier_code_str
[al
->hier
.code
];
852 out
= al
->http
.content_type
;
856 case LFT_CLIENT_REQ_METHOD
:
858 out
= al
->request
->method
.image();
863 case LFT_CLIENT_REQ_URI
:
864 // original client URI
866 out
= urlCanonical(al
->request
);
871 case LFT_REQUEST_URLPATH_OLD_31
:
872 case LFT_CLIENT_REQ_URLPATH
:
874 out
= al
->request
->urlpath
.termedBuf();
879 case LFT_CLIENT_REQ_VERSION
:
881 snprintf(tmp
, sizeof(tmp
), "%d.%d", (int) al
->request
->http_ver
.major
, (int) al
->request
->http_ver
.minor
);
886 case LFT_REQUEST_METHOD
:
887 out
= al
->_private
.method_str
;
890 case LFT_REQUEST_URI
:
894 case LFT_REQUEST_VERSION_OLD_2X
:
895 case LFT_REQUEST_VERSION
:
896 snprintf(tmp
, sizeof(tmp
), "%d.%d", (int) al
->http
.version
.major
, (int) al
->http
.version
.minor
);
900 case LFT_SERVER_REQ_METHOD
:
901 if (al
->adapted_request
) {
902 out
= al
->adapted_request
->method
.image();
907 case LFT_SERVER_REQ_URI
:
908 // adapted request URI sent to server/peer
909 if (al
->adapted_request
) {
910 out
= urlCanonical(al
->adapted_request
);
915 case LFT_SERVER_REQ_URLPATH
:
916 if (al
->adapted_request
) {
917 out
= al
->adapted_request
->urlpath
.termedBuf();
922 case LFT_SERVER_REQ_VERSION
:
923 if (al
->adapted_request
) {
924 snprintf(tmp
, sizeof(tmp
), "%d.%d",
925 (int) al
->adapted_request
->http_ver
.major
,
926 (int) al
->adapted_request
->http_ver
.minor
);
931 case LFT_REQUEST_SIZE_TOTAL
:
932 outoff
= al
->cache
.requestSize
;
936 /*case LFT_REQUEST_SIZE_LINE: */
937 case LFT_REQUEST_SIZE_HEADERS
:
938 outoff
= al
->cache
.requestHeadersSize
;
941 /*case LFT_REQUEST_SIZE_BODY: */
942 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
944 case LFT_REPLY_SIZE_TOTAL
:
945 outoff
= al
->cache
.replySize
;
949 case LFT_REPLY_HIGHOFFSET
:
950 outoff
= al
->cache
.highOffset
;
956 case LFT_REPLY_OBJECTSIZE
:
957 outoff
= al
->cache
.objectSize
;
963 /*case LFT_REPLY_SIZE_LINE: */
964 case LFT_REPLY_SIZE_HEADERS
:
965 outint
= al
->cache
.replyHeadersSize
;
968 /*case LFT_REPLY_SIZE_BODY: */
969 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
973 out
= al
->request
->tag
.termedBuf();
979 case LFT_IO_SIZE_TOTAL
:
980 outint
= al
->cache
.requestSize
+ al
->cache
.replySize
;
986 out
= al
->request
->extacl_log
.termedBuf();
992 case LFT_SEQUENCE_NUMBER
:
993 outoff
= logSequenceNumber
;
1004 snprintf(tmp
, sizeof(tmp
), "%0*" PRId64
, fmt
->zero
? (int) fmt
->width
: 0, outoff
);
1008 snprintf(tmp
, sizeof(tmp
), "%0*ld", fmt
->zero
? (int) fmt
->width
: 0, outint
);
1013 if (quote
|| fmt
->quote
!= LOG_QUOTE_NONE
) {
1014 char *newout
= NULL
;
1017 switch (fmt
->quote
) {
1019 case LOG_QUOTE_NONE
:
1020 newout
= rfc1738_escape_unescaped(out
);
1023 case LOG_QUOTE_QUOTES
: {
1024 size_t out_len
= static_cast<size_t>(strlen(out
)) * 2 + 1;
1025 if (out_len
>= sizeof(tmp
)) {
1026 newout
= (char *)xmalloc(out_len
);
1030 log_quoted_string(out
, newout
);
1034 case LOG_QUOTE_MIMEBLOB
:
1035 newout
= QuoteMimeBlob(out
);
1040 newout
= rfc1738_escape(out
);
1059 mb
.Printf("%-*s", (int) fmt
->width
, out
);
1061 mb
.Printf("%*s", (int) fmt
->width
, out
);
1063 mb
.append(out
, strlen(out
));