1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /***************************************************************************
25 LogField.cc
26
27 This file implements the LogField object, which is the central
28 representation of a logging field.
29 ***************************************************************************/
30 #include "tscore/ink_platform.h"
31
32 #include "MIME.h"
33 #include "LogUtils.h"
34 #include "LogField.h"
35 #include "LogBuffer.h"
36 #include "LogAccess.h"
37 #include "Log.h"
38
39 // clang-format off
40 //
41 static const char *container_names[] = {
42 "not-a-container",
43 "cqh",
44 "psh",
45 "pqh",
46 "ssh",
47 "cssh",
48 "ecqh",
49 "epsh",
50 "epqh",
51 "essh",
52 "ecssh",
53 "icfg",
54 "scfg",
55 "record",
56 "ms",
57 "msdms",
58 };
59
60 static const char *aggregate_names[] = {
61 "not-an-agg-op",
62 "COUNT",
63 "SUM",
64 "AVG",
65 "FIRST",
66 "LAST",
67 };
68
69 // clang-format on
70
LogSlice(char * str)71 LogSlice::LogSlice(char *str)
72 {
73 char *a, *b, *c;
74
75 m_enable = false;
76 m_start = 0;
77 m_end = INT_MAX;
78
79 if ((a = strchr(str, '[')) == nullptr) {
80 return;
81 }
82
83 *a++ = '\0';
84 if ((b = strchr(a, ':')) == nullptr) {
85 return;
86 }
87
88 *b++ = '\0';
89 if ((c = strchr(b, ']')) == nullptr) {
90 return;
91 }
92
93 m_enable = true;
94
95 // eat space
96 while (a != b && *a == ' ') {
97 a++;
98 }
99
100 if (a != b) {
101 m_start = atoi(a);
102 }
103
104 // eat space
105 while (b != c && *b == ' ') {
106 b++;
107 }
108
109 if (b != c) {
110 m_end = atoi(b);
111 }
112 }
113
114 int
toStrOffset(int strlen,int * offset)115 LogSlice::toStrOffset(int strlen, int *offset)
116 {
117 int i, j, len;
118
119 // letf index
120 if (m_start >= 0) {
121 i = m_start;
122 } else {
123 i = m_start + strlen;
124 }
125
126 if (i >= strlen) {
127 return 0;
128 }
129
130 if (i < 0) {
131 i = 0;
132 }
133
134 // right index
135 if (m_end >= 0) {
136 j = m_end;
137 } else {
138 j = m_end + strlen;
139 }
140
141 if (j <= 0) {
142 return 0;
143 }
144
145 if (j > strlen) {
146 j = strlen;
147 }
148
149 // available length
150 len = j - i;
151
152 if (len > 0) {
153 *offset = i;
154 }
155
156 return len;
157 }
158
159 /*-------------------------------------------------------------------------
160 LogField::LogField
161 -------------------------------------------------------------------------*/
162
163 namespace
164 {
165 struct cmp_str {
166 bool
operator ()__anon9b62b7cd0111::cmp_str167 operator()(ts::ConstBuffer a, ts::ConstBuffer b) const
168 {
169 return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0;
170 }
171 };
172 } // namespace
173
174 using milestone_map = std::map<ts::ConstBuffer, TSMilestonesType, cmp_str>;
175 static milestone_map m_milestone_map;
176
177 struct milestone {
178 const char *msname;
179 TSMilestonesType mstype;
180 };
181
182 static const milestone milestones[] = {
183 {"TS_MILESTONE_UA_BEGIN", TS_MILESTONE_UA_BEGIN},
184 {"TS_MILESTONE_UA_FIRST_READ", TS_MILESTONE_UA_FIRST_READ},
185 {"TS_MILESTONE_UA_READ_HEADER_DONE", TS_MILESTONE_UA_READ_HEADER_DONE},
186 {"TS_MILESTONE_UA_BEGIN_WRITE", TS_MILESTONE_UA_BEGIN_WRITE},
187 {"TS_MILESTONE_UA_CLOSE", TS_MILESTONE_UA_CLOSE},
188 {"TS_MILESTONE_SERVER_FIRST_CONNECT", TS_MILESTONE_SERVER_FIRST_CONNECT},
189 {"TS_MILESTONE_SERVER_CONNECT", TS_MILESTONE_SERVER_CONNECT},
190 {"TS_MILESTONE_SERVER_CONNECT_END", TS_MILESTONE_SERVER_CONNECT_END},
191 {"TS_MILESTONE_SERVER_BEGIN_WRITE", TS_MILESTONE_SERVER_BEGIN_WRITE},
192 {"TS_MILESTONE_SERVER_FIRST_READ", TS_MILESTONE_SERVER_FIRST_READ},
193 {"TS_MILESTONE_SERVER_READ_HEADER_DONE", TS_MILESTONE_SERVER_READ_HEADER_DONE},
194 {"TS_MILESTONE_SERVER_CLOSE", TS_MILESTONE_SERVER_CLOSE},
195 {"TS_MILESTONE_CACHE_OPEN_READ_BEGIN", TS_MILESTONE_CACHE_OPEN_READ_BEGIN},
196 {"TS_MILESTONE_CACHE_OPEN_READ_END", TS_MILESTONE_CACHE_OPEN_READ_END},
197 {"TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN", TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN},
198 {"TS_MILESTONE_CACHE_OPEN_WRITE_END", TS_MILESTONE_CACHE_OPEN_WRITE_END},
199 {"TS_MILESTONE_DNS_LOOKUP_BEGIN", TS_MILESTONE_DNS_LOOKUP_BEGIN},
200 {"TS_MILESTONE_DNS_LOOKUP_END", TS_MILESTONE_DNS_LOOKUP_END},
201 {"TS_MILESTONE_SM_START", TS_MILESTONE_SM_START},
202 {"TS_MILESTONE_SM_FINISH", TS_MILESTONE_SM_FINISH},
203 {"TS_MILESTONE_PLUGIN_ACTIVE", TS_MILESTONE_PLUGIN_ACTIVE},
204 {"TS_MILESTONE_PLUGIN_TOTAL", TS_MILESTONE_PLUGIN_TOTAL},
205 {"TS_MILESTONE_TLS_HANDSHAKE_START", TS_MILESTONE_TLS_HANDSHAKE_START},
206 {"TS_MILESTONE_TLS_HANDSHAKE_END", TS_MILESTONE_TLS_HANDSHAKE_END},
207 };
208
209 void
init_milestone_container()210 LogField::init_milestone_container()
211 {
212 if (m_milestone_map.empty()) {
213 for (unsigned i = 0; i < countof(milestones); ++i) {
214 m_milestone_map.insert(
215 std::make_pair(ts::ConstBuffer(milestones[i].msname, strlen(milestones[i].msname)), milestones[i].mstype));
216 }
217 }
218 }
219
220 // Generic field ctor
LogField(const char * name,const char * symbol,Type type,MarshalFunc marshal,UnmarshalFunc unmarshal,SetFunc _setfunc)221 LogField::LogField(const char *name, const char *symbol, Type type, MarshalFunc marshal, UnmarshalFunc unmarshal, SetFunc _setfunc)
222 : m_name(ats_strdup(name)),
223 m_symbol(ats_strdup(symbol)),
224 m_type(type),
225 m_container(NO_CONTAINER),
226 m_marshal_func(marshal),
227 m_unmarshal_func(unmarshal),
228 m_unmarshal_func_map(nullptr),
229 m_agg_op(NO_AGGREGATE),
230 m_agg_cnt(0),
231 m_agg_val(0),
232 m_milestone1(TS_MILESTONE_LAST_ENTRY),
233 m_milestone2(TS_MILESTONE_LAST_ENTRY),
234 m_time_field(false),
235 m_alias_map(nullptr),
236 m_set_func(_setfunc)
237 {
238 ink_assert(m_name != nullptr);
239 ink_assert(m_symbol != nullptr);
240 ink_assert(m_type >= 0 && m_type < N_TYPES);
241 ink_assert(m_marshal_func != (MarshalFunc) nullptr);
242
243 m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
244 strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
245 }
246
LogField(const char * name,const char * symbol,Type type,MarshalFunc marshal,UnmarshalFuncWithMap unmarshal,const Ptr<LogFieldAliasMap> & map,SetFunc _setfunc)247 LogField::LogField(const char *name, const char *symbol, Type type, MarshalFunc marshal, UnmarshalFuncWithMap unmarshal,
248 const Ptr<LogFieldAliasMap> &map, SetFunc _setfunc)
249 : m_name(ats_strdup(name)),
250 m_symbol(ats_strdup(symbol)),
251 m_type(type),
252 m_container(NO_CONTAINER),
253 m_marshal_func(marshal),
254 m_unmarshal_func(nullptr),
255 m_unmarshal_func_map(unmarshal),
256 m_agg_op(NO_AGGREGATE),
257 m_agg_cnt(0),
258 m_agg_val(0),
259 m_milestone1(TS_MILESTONE_LAST_ENTRY),
260 m_milestone2(TS_MILESTONE_LAST_ENTRY),
261 m_time_field(false),
262 m_alias_map(map),
263 m_set_func(_setfunc)
264 {
265 ink_assert(m_name != nullptr);
266 ink_assert(m_symbol != nullptr);
267 ink_assert(m_type >= 0 && m_type < N_TYPES);
268 ink_assert(m_marshal_func != (MarshalFunc) nullptr);
269 ink_assert(m_alias_map);
270
271 m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
272 strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
273 }
274
275 TSMilestonesType
milestone_from_m_name()276 LogField::milestone_from_m_name()
277 {
278 milestone_map::iterator it;
279 TSMilestonesType result = TS_MILESTONE_LAST_ENTRY;
280
281 it = m_milestone_map.find(ts::ConstBuffer(m_name, strlen(m_name)));
282 if (it != m_milestone_map.end()) {
283 result = it->second;
284 }
285
286 return result;
287 }
288
289 int
milestones_from_m_name(TSMilestonesType * ms1,TSMilestonesType * ms2)290 LogField::milestones_from_m_name(TSMilestonesType *ms1, TSMilestonesType *ms2)
291 {
292 milestone_map::iterator it;
293 ts::ConstBuffer ms1_name, ms2_name(m_name, strlen(m_name));
294
295 ms1_name = ms2_name.splitOn('-');
296
297 it = m_milestone_map.find(ms1_name);
298 if (it != m_milestone_map.end()) {
299 *ms1 = it->second;
300 } else {
301 return -1;
302 }
303
304 it = m_milestone_map.find(ms2_name);
305 if (it != m_milestone_map.end()) {
306 *ms2 = it->second;
307 } else {
308 return -1;
309 }
310
311 return 0;
312 }
313
314 // Container field ctor
LogField(const char * field,Container container,SetFunc _setfunc)315 LogField::LogField(const char *field, Container container, SetFunc _setfunc)
316 : m_name(ats_strdup(field)),
317 m_symbol(ats_strdup(container_names[container])),
318 m_type(LogField::STRING),
319 m_container(container),
320 m_marshal_func(nullptr),
321 m_unmarshal_func(nullptr),
322 m_unmarshal_func_map(nullptr),
323 m_agg_op(NO_AGGREGATE),
324 m_agg_cnt(0),
325 m_agg_val(0),
326 m_milestone1(TS_MILESTONE_LAST_ENTRY),
327 m_milestone2(TS_MILESTONE_LAST_ENTRY),
328 m_time_field(false),
329 m_alias_map(nullptr),
330 m_set_func(nullptr)
331 {
332 ink_assert(m_name != nullptr);
333 ink_assert(m_symbol != nullptr);
334 ink_assert(m_type >= 0 && m_type < N_TYPES);
335
336 m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
337 strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
338
339 switch (m_container) {
340 case CQH:
341 case PSH:
342 case PQH:
343 case SSH:
344 case CSSH:
345 case ECQH:
346 case EPSH:
347 case EPQH:
348 case ESSH:
349 case ECSSH:
350 case SCFG:
351 m_unmarshal_func = reinterpret_cast<UnmarshalFunc>(&(LogAccess::unmarshal_str));
352 break;
353
354 case ICFG:
355 m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
356 break;
357
358 case RECORD:
359 m_unmarshal_func = &(LogAccess::unmarshal_record);
360 break;
361
362 case MS:
363 m_milestone1 = milestone_from_m_name();
364 if (TS_MILESTONE_LAST_ENTRY == m_milestone1) {
365 Note("Invalid milestone name in LogField ctor: %s", m_name);
366 }
367 m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
368 break;
369
370 case MSDMS: {
371 int rv = milestones_from_m_name(&m_milestone1, &m_milestone2);
372 if (0 != rv) {
373 Note("Invalid milestone range in LogField ctor: %s", m_name);
374 }
375 m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
376 break;
377 }
378
379 default:
380 Note("Invalid container type in LogField ctor: %d", container);
381 }
382 }
383
384 // Copy ctor
LogField(const LogField & rhs)385 LogField::LogField(const LogField &rhs)
386 : m_name(ats_strdup(rhs.m_name)),
387 m_symbol(ats_strdup(rhs.m_symbol)),
388 m_type(rhs.m_type),
389 m_container(rhs.m_container),
390 m_marshal_func(rhs.m_marshal_func),
391 m_unmarshal_func(rhs.m_unmarshal_func),
392 m_unmarshal_func_map(rhs.m_unmarshal_func_map),
393 m_agg_op(rhs.m_agg_op),
394 m_agg_cnt(0),
395 m_agg_val(0),
396 m_milestone1(TS_MILESTONE_LAST_ENTRY),
397 m_milestone2(TS_MILESTONE_LAST_ENTRY),
398 m_time_field(rhs.m_time_field),
399 m_alias_map(rhs.m_alias_map),
400 m_set_func(rhs.m_set_func)
401 {
402 ink_assert(m_name != nullptr);
403 ink_assert(m_symbol != nullptr);
404 ink_assert(m_type >= 0 && m_type < N_TYPES);
405 }
406
407 /*-------------------------------------------------------------------------
408 LogField::~LogField
409 -------------------------------------------------------------------------*/
~LogField()410 LogField::~LogField()
411 {
412 ats_free(m_name);
413 ats_free(m_symbol);
414 }
415
416 /*-------------------------------------------------------------------------
417 LogField::marshal_len
418
419 This routine will find the marshalling length (in bytes) for this field,
420 given a LogAccess object. It does this by using the property of the
421 marshalling routines that if the marshal buffer is NULL, only the size
422 requirement is returned.
423 -------------------------------------------------------------------------*/
424 unsigned
marshal_len(LogAccess * lad)425 LogField::marshal_len(LogAccess *lad)
426 {
427 if (m_container == NO_CONTAINER) {
428 return (lad->*m_marshal_func)(nullptr);
429 }
430
431 switch (m_container) {
432 case CQH:
433 case PSH:
434 case PQH:
435 case SSH:
436 case CSSH:
437 return lad->marshal_http_header_field(m_container, m_name, nullptr);
438
439 case ECQH:
440 case EPSH:
441 case EPQH:
442 case ESSH:
443 case ECSSH:
444 return lad->marshal_http_header_field_escapify(m_container, m_name, nullptr);
445
446 case ICFG:
447 return lad->marshal_config_int_var(m_name, nullptr);
448
449 case SCFG:
450 return lad->marshal_config_str_var(m_name, nullptr);
451
452 case RECORD:
453 return lad->marshal_record(m_name, nullptr);
454
455 case MS:
456 return lad->marshal_milestone(m_milestone1, nullptr);
457
458 case MSDMS:
459 return lad->marshal_milestone_diff(m_milestone1, m_milestone2, nullptr);
460
461 default:
462 return 0;
463 }
464 }
465
466 bool
isContainerUpdateFieldSupported(Container container)467 LogField::isContainerUpdateFieldSupported(Container container)
468 {
469 switch (container) {
470 case CQH:
471 case PSH:
472 case PQH:
473 case SSH:
474 case CSSH:
475 case ECQH:
476 case EPSH:
477 case EPQH:
478 case ESSH:
479 case ECSSH:
480 case SCFG:
481 return true;
482 default:
483 return false;
484 }
485 }
486
487 void
updateField(LogAccess * lad,char * buf,int len)488 LogField::updateField(LogAccess *lad, char *buf, int len)
489 {
490 if (m_container == NO_CONTAINER) {
491 return (lad->*m_set_func)(buf, len);
492 } else {
493 if (isContainerUpdateFieldSupported(m_container)) {
494 return set_http_header_field(lad, m_container, this->m_name, buf, len);
495 } else {
496 // no set function defined for the container
497 }
498 }
499 }
500
501 /*-------------------------------------------------------------------------
502 LogField::marshal
503
504 This routine will marshsal the given field into the buffer provided.
505 -------------------------------------------------------------------------*/
506 unsigned
marshal(LogAccess * lad,char * buf)507 LogField::marshal(LogAccess *lad, char *buf)
508 {
509 if (m_container == NO_CONTAINER) {
510 return (lad->*m_marshal_func)(buf);
511 }
512
513 switch (m_container) {
514 case CQH:
515 case PSH:
516 case PQH:
517 case SSH:
518 case CSSH:
519 return lad->marshal_http_header_field(m_container, m_name, buf);
520
521 case ECQH:
522 case EPSH:
523 case EPQH:
524 case ESSH:
525 case ECSSH:
526 return lad->marshal_http_header_field_escapify(m_container, m_name, buf);
527
528 case ICFG:
529 return lad->marshal_config_int_var(m_name, buf);
530
531 case SCFG:
532 return lad->marshal_config_str_var(m_name, buf);
533
534 case RECORD:
535 return lad->marshal_record(m_name, buf);
536
537 case MS:
538 return lad->marshal_milestone(m_milestone1, buf);
539
540 case MSDMS:
541 return lad->marshal_milestone_diff(m_milestone1, m_milestone2, buf);
542
543 default:
544 return 0;
545 }
546 }
547
548 /*-------------------------------------------------------------------------
549 LogField::marshal_agg
550 -------------------------------------------------------------------------*/
551 unsigned
marshal_agg(char * buf)552 LogField::marshal_agg(char *buf)
553 {
554 ink_assert(buf != nullptr);
555 int64_t avg = 0;
556
557 switch (m_agg_op) {
558 case eCOUNT:
559 LogAccess::marshal_int(buf, m_agg_cnt);
560 break;
561
562 case eSUM:
563 case eFIRST:
564 case eLAST:
565 LogAccess::marshal_int(buf, m_agg_val);
566 break;
567
568 case eAVG:
569 if (m_agg_cnt) {
570 avg = m_agg_val / m_agg_cnt;
571 }
572 LogAccess::marshal_int(buf, avg);
573 break;
574
575 default:
576 Note("Cannot marshal aggregate field %s; "
577 "invalid aggregate operator: %d",
578 m_symbol, m_agg_op);
579 return 0;
580 }
581
582 m_agg_val = 0;
583 m_agg_cnt = 0;
584
585 return INK_MIN_ALIGN;
586 }
587
588 /*-------------------------------------------------------------------------
589 LogField::unmarshal
590
591 This routine will invoke the proper unmarshalling routine to return a
592 string that represents the ASCII value of the field.
593 -------------------------------------------------------------------------*/
594 unsigned
unmarshal(char ** buf,char * dest,int len)595 LogField::unmarshal(char **buf, char *dest, int len)
596 {
597 if (!m_alias_map) {
598 if (m_unmarshal_func == reinterpret_cast<UnmarshalFunc>(LogAccess::unmarshal_str) ||
599 m_unmarshal_func == reinterpret_cast<UnmarshalFunc>(LogAccess::unmarshal_http_text)) {
600 UnmarshalFuncWithSlice func = reinterpret_cast<UnmarshalFuncWithSlice>(m_unmarshal_func);
601 return (*func)(buf, dest, len, &m_slice);
602 }
603 return (*m_unmarshal_func)(buf, dest, len);
604 } else {
605 return (*m_unmarshal_func_map)(buf, dest, len, m_alias_map);
606 }
607 }
608
609 /*-------------------------------------------------------------------------
610 LogField::display
611 -------------------------------------------------------------------------*/
612 void
display(FILE * fd)613 LogField::display(FILE *fd)
614 {
615 static const char *names[LogField::N_TYPES] = {"sINT", "dINT", "STR", "IP"};
616
617 fprintf(fd, " %30s %10s %5s\n", m_name, m_symbol, names[m_type]);
618 }
619
620 /*-------------------------------------------------------------------------
621 LogField::operator==
622
623 This operator does only care of the name and m_symbol, may need
624 do check on others layter.
625 -------------------------------------------------------------------------*/
626 bool
operator ==(LogField & rhs)627 LogField::operator==(LogField &rhs)
628 {
629 if (strcmp(name(), rhs.name()) || strcmp(symbol(), rhs.symbol())) {
630 return false;
631 } else {
632 return true;
633 }
634 }
635
636 /*-------------------------------------------------------------------------
637 -------------------------------------------------------------------------*/
638 void
set_aggregate_op(LogField::Aggregate agg_op)639 LogField::set_aggregate_op(LogField::Aggregate agg_op)
640 {
641 if (agg_op > 0 && agg_op < LogField::N_AGGREGATES) {
642 m_agg_op = agg_op;
643 } else {
644 Note("Invalid aggregate operator identifier: %d", agg_op);
645 m_agg_op = NO_AGGREGATE;
646 }
647 }
648
649 void
update_aggregate(int64_t val)650 LogField::update_aggregate(int64_t val)
651 {
652 switch (m_agg_op) {
653 case eCOUNT:
654 case eSUM:
655 case eAVG:
656 m_agg_val += val;
657 m_agg_cnt++;
658 break;
659
660 case eFIRST:
661 if (m_agg_cnt == 0) {
662 m_agg_val = val;
663 m_agg_cnt++;
664 }
665 break;
666
667 case eLAST:
668 m_agg_val = val;
669 m_agg_cnt++;
670 break;
671
672 default:
673 Note("Cannot update aggregate field; invalid operator %d", m_agg_op);
674 return;
675 }
676
677 Debug("log-agg",
678 "Aggregate field %s updated with val %" PRId64 ", "
679 "new val = %" PRId64 ", cnt = %" PRId64 "",
680 m_symbol, val, m_agg_val, m_agg_cnt);
681 }
682
683 LogField::Container
valid_container_name(char * name)684 LogField::valid_container_name(char *name)
685 {
686 for (unsigned i = 1; i < countof(container_names); i++) {
687 if (strcmp(name, container_names[i]) == 0) {
688 return static_cast<LogField::Container>(i);
689 }
690 }
691
692 return LogField::NO_CONTAINER;
693 }
694
695 LogField::Aggregate
valid_aggregate_name(char * name)696 LogField::valid_aggregate_name(char *name)
697 {
698 for (unsigned i = 1; i < countof(aggregate_names); i++) {
699 if (strcmp(name, aggregate_names[i]) == 0) {
700 return static_cast<LogField::Aggregate>(i);
701 }
702 }
703
704 return LogField::NO_AGGREGATE;
705 }
706
707 bool
fieldlist_contains_aggregates(const char * fieldlist)708 LogField::fieldlist_contains_aggregates(const char *fieldlist)
709 {
710 const char *match;
711
712 for (unsigned i = 1; i < countof(aggregate_names); i++) {
713 if ((match = strstr(fieldlist, aggregate_names[i])) != nullptr) {
714 // verify that the aggregate string is not part of a container field name.
715 if ((strchr(fieldlist, '{') == nullptr) && (strchr(match, '}') == nullptr)) {
716 return true;
717 }
718 }
719 }
720
721 return false;
722 }
723
724 void
set_http_header_field(LogAccess * lad,LogField::Container container,char * field,char * buf,int len)725 LogField::set_http_header_field(LogAccess *lad, LogField::Container container, char *field, char *buf, int len)
726 {
727 return lad->set_http_header_field(container, field, buf, len);
728 }
729
730 /*-------------------------------------------------------------------------
731 LogFieldList
732
733 It is ASSUMED that each element on this list has been allocated from the
734 heap with "new" and that each element is on at most ONE list. To enforce
735 this, items are copied by default, using the copy ctor.
736 -------------------------------------------------------------------------*/
737 LogFieldList::LogFieldList() = default;
738
~LogFieldList()739 LogFieldList::~LogFieldList()
740 {
741 clear();
742 }
743
744 void
clear()745 LogFieldList::clear()
746 {
747 LogField *f;
748 while ((f = m_field_list.dequeue())) {
749 delete f; // safe given the semantics stated above
750 }
751 m_marshal_len = 0;
752 _badSymbols.clear();
753 }
754
755 void
add(LogField * field,bool copy)756 LogFieldList::add(LogField *field, bool copy)
757 {
758 ink_assert(field != nullptr);
759
760 if (copy) {
761 m_field_list.enqueue(new LogField(*field));
762 } else {
763 m_field_list.enqueue(field);
764 }
765
766 if (field->type() == LogField::sINT) {
767 m_marshal_len += INK_MIN_ALIGN;
768 }
769 }
770
771 LogField *
find_by_name(const char * name) const772 LogFieldList::find_by_name(const char *name) const
773 {
774 for (LogField *f = first(); f; f = next(f)) {
775 if (!strcmp(f->name(), name)) {
776 return f;
777 }
778 }
779 return nullptr;
780 }
781
782 LogField *
find_by_symbol(const char * symbol) const783 LogFieldList::find_by_symbol(const char *symbol) const
784 {
785 LogField *field = nullptr;
786
787 if (auto it = Log::field_symbol_hash.find(symbol); it != Log::field_symbol_hash.end() && it->second) {
788 field = it->second;
789 Debug("log-field-hash", "Field %s found", field->symbol());
790 return field;
791 }
792 // trusty old method
793
794 for (field = first(); field; field = next(field)) {
795 if (!strcmp(field->symbol(), symbol)) {
796 return field;
797 }
798 }
799
800 return nullptr;
801 }
802
803 unsigned
marshal_len(LogAccess * lad)804 LogFieldList::marshal_len(LogAccess *lad)
805 {
806 int bytes = 0;
807 for (LogField *f = first(); f; f = next(f)) {
808 if (f->type() != LogField::sINT) {
809 const int len = f->marshal_len(lad);
810 ink_release_assert(len >= INK_MIN_ALIGN);
811 bytes += len;
812 }
813 }
814 return m_marshal_len + bytes;
815 }
816
817 unsigned
marshal(LogAccess * lad,char * buf)818 LogFieldList::marshal(LogAccess *lad, char *buf)
819 {
820 char *ptr;
821 int bytes = 0;
822 for (LogField *f = first(); f; f = next(f)) {
823 ptr = &buf[bytes];
824 bytes += f->marshal(lad, ptr);
825 ink_assert(bytes % INK_MIN_ALIGN == 0);
826 }
827 return bytes;
828 }
829
830 unsigned
marshal_agg(char * buf)831 LogFieldList::marshal_agg(char *buf)
832 {
833 char *ptr;
834 int bytes = 0;
835 for (LogField *f = first(); f; f = next(f)) {
836 ptr = &buf[bytes];
837 bytes += f->marshal_agg(ptr);
838 }
839 return bytes;
840 }
841
842 unsigned
count()843 LogFieldList::count()
844 {
845 unsigned cnt = 0;
846 for (LogField *f = first(); f; f = next(f)) {
847 cnt++;
848 }
849 return cnt;
850 }
851
852 void
display(FILE * fd)853 LogFieldList::display(FILE *fd)
854 {
855 for (LogField *f = first(); f; f = next(f)) {
856 f->display(fd);
857 }
858 }
859
860 void
addBadSymbol(std::string_view badSymbol)861 LogFieldList::addBadSymbol(std::string_view badSymbol)
862 {
863 if (_badSymbols.size() > 0) {
864 _badSymbols += ", ";
865 }
866 _badSymbols += badSymbol;
867 }
868