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 LogFormat.cc
26
27
28 ***************************************************************************/
29 #include "tscore/ink_config.h"
30
31 #include <cstdio>
32 #include <cstring>
33 #include <cstdlib>
34
35 #include "tscore/SimpleTokenizer.h"
36 #include "tscore/CryptoHash.h"
37
38 #include "LogUtils.h"
39 #include "LogFile.h"
40 #include "LogField.h"
41 #include "LogFilter.h"
42 #include "LogFormat.h"
43 #include "LogBuffer.h"
44 #include "LogObject.h"
45 #include "LogConfig.h"
46 #include "Log.h"
47
48 // class variables
49 //
50 bool LogFormat::m_tagging_on = false;
51
52 /*-------------------------------------------------------------------------
53 LogFormat::setup
54 -------------------------------------------------------------------------*/
55
56 bool
setup(const char * name,const char * format_str,unsigned interval_sec)57 LogFormat::setup(const char *name, const char *format_str, unsigned interval_sec)
58 {
59 if (name == nullptr) {
60 Note("missing log format name");
61 return false;
62 }
63
64 if (format_str) {
65 const char *tag = " %<phn>";
66 const size_t m_format_str_size = strlen(format_str) + (m_tagging_on ? strlen(tag) : 0) + 1;
67 m_format_str = static_cast<char *>(ats_malloc(m_format_str_size));
68 ink_strlcpy(m_format_str, format_str, m_format_str_size);
69 if (m_tagging_on) {
70 Note("Log tagging enabled, adding %%<phn> field at the end of "
71 "format %s",
72 name);
73 ink_strlcat(m_format_str, tag, m_format_str_size);
74 };
75
76 char *printf_str = nullptr;
77 char *fieldlist_str = nullptr;
78 int nfields = parse_format_string(m_format_str, &printf_str, &fieldlist_str);
79 if (nfields > (m_tagging_on ? 1 : 0)) {
80 init_variables(name, fieldlist_str, printf_str, interval_sec);
81 } else {
82 Note("Format %s encountered an error parsing the symbol string "
83 "\"%s\", symbol string contains no fields",
84 ((name) ? name : "no-name"), format_str);
85 m_valid = false;
86 }
87
88 ats_free(fieldlist_str);
89 ats_free(printf_str);
90
91 // We are only valid if init_variables() says we are.
92 return true;
93 }
94
95 // We don't have a format string (ie. this will be a raw text log, so we are always valid.
96 m_valid = true;
97 return true;
98 }
99
100 /*-------------------------------------------------------------------------
101 LogFormat::id_from_name
102 -------------------------------------------------------------------------*/
103
104 int32_t
id_from_name(const char * name)105 LogFormat::id_from_name(const char *name)
106 {
107 int32_t id = 0;
108 if (name) {
109 CryptoHash hash;
110 CryptoContext().hash_immediate(hash, name, static_cast<int>(strlen(name)));
111 #if defined(linux)
112 /* Mask most significant bit so that return value of this function
113 * is not sign extended to be a negative number.
114 * This problem is only known to occur on Linux which
115 * is a 32-bit OS.
116 */
117 id = static_cast<int32_t>(hash.fold()) & 0x7fffffff;
118 #else
119 id = (int32_t)hash.fold();
120 #endif
121 }
122 return id;
123 }
124
125 /*-------------------------------------------------------------------------
126 LogFormat::init_variables
127 -------------------------------------------------------------------------*/
128
129 void
init_variables(const char * name,const char * fieldlist_str,const char * printf_str,unsigned interval_sec)130 LogFormat::init_variables(const char *name, const char *fieldlist_str, const char *printf_str, unsigned interval_sec)
131 {
132 m_field_count = parse_symbol_string(fieldlist_str, &m_field_list, &m_aggregate);
133
134 if (m_field_count == 0) {
135 m_valid = false;
136 } else if (m_aggregate && !interval_sec) {
137 Note("Format for aggregate operators but no interval "
138 "was specified");
139 m_valid = false;
140 } else {
141 if (m_aggregate) {
142 m_agg_marshal_space = static_cast<char *>(ats_malloc(m_field_count * INK_MIN_ALIGN));
143 }
144
145 if (m_name_str) {
146 ats_free(m_name_str);
147 m_name_str = nullptr;
148 m_name_id = 0;
149 }
150 if (name) {
151 m_name_str = ats_strdup(name);
152 m_name_id = id_from_name(m_name_str);
153 }
154
155 if (m_fieldlist_str) {
156 ats_free(m_fieldlist_str);
157 m_fieldlist_str = nullptr;
158 m_fieldlist_id = 0;
159 }
160 if (fieldlist_str) {
161 m_fieldlist_str = ats_strdup(fieldlist_str);
162 m_fieldlist_id = id_from_name(m_fieldlist_str);
163 }
164
165 m_printf_str = ats_strdup(printf_str);
166 m_interval_sec = interval_sec;
167 m_interval_next = LogUtils::timestamp();
168
169 m_valid = true;
170 }
171 }
172
173 /*-------------------------------------------------------------------------
174 LogFormat::LogFormat
175
176 This is the general ctor that builds a LogFormat object from the data
177 provided. In this case, the "fields" character string is a printf-style
178 string where the field symbols are represented within the string in the
179 form %<symbol>.
180 -------------------------------------------------------------------------*/
181
LogFormat(const char * name,const char * format_str,unsigned interval_sec)182 LogFormat::LogFormat(const char *name, const char *format_str, unsigned interval_sec)
183 : m_interval_sec(0),
184 m_interval_next(0),
185 m_agg_marshal_space(nullptr),
186 m_valid(false),
187 m_name_str(nullptr),
188 m_name_id(0),
189 m_fieldlist_str(nullptr),
190 m_fieldlist_id(0),
191 m_field_count(0),
192 m_printf_str(nullptr),
193 m_aggregate(false),
194 m_format_str(nullptr)
195 {
196 setup(name, format_str, interval_sec);
197
198 // A LOG_FORMAT_TEXT is a log without a format string, everything else is a LOG_FORMAT_CUSTOM. It's possible that we could get
199 // rid of log types altogether, but LogFile currently tests whether a format is a LOG_FORMAT_TEXT format ...
200 m_format_type = format_str ? LOG_FORMAT_CUSTOM : LOG_FORMAT_TEXT;
201 }
202
203 /*-------------------------------------------------------------------------
204 LogFormat::LogFormat
205
206 This is the copy ctor, needed for copying lists of Format objects.
207 -------------------------------------------------------------------------*/
208
LogFormat(const LogFormat & rhs)209 LogFormat::LogFormat(const LogFormat &rhs)
210 : RefCountObj(rhs),
211 m_interval_sec(0),
212 m_interval_next(0),
213 m_agg_marshal_space(nullptr),
214 m_valid(rhs.m_valid),
215 m_name_str(nullptr),
216 m_name_id(0),
217 m_fieldlist_str(nullptr),
218 m_fieldlist_id(0),
219 m_field_count(0),
220 m_printf_str(nullptr),
221 m_aggregate(false),
222 m_format_str(nullptr),
223 m_format_type(rhs.m_format_type)
224 {
225 if (m_valid) {
226 if (m_format_type == LOG_FORMAT_TEXT) {
227 m_name_str = ats_strdup(rhs.m_name_str);
228 } else {
229 m_format_str = rhs.m_format_str ? ats_strdup(rhs.m_format_str) : nullptr;
230 init_variables(rhs.m_name_str, rhs.m_fieldlist_str, rhs.m_printf_str, rhs.m_interval_sec);
231 }
232 }
233 }
234
235 /*-------------------------------------------------------------------------
236 LogFormat::~LogFormat
237 -------------------------------------------------------------------------*/
238
~LogFormat()239 LogFormat::~LogFormat()
240 {
241 ats_free(m_name_str);
242 ats_free(m_fieldlist_str);
243 ats_free(m_printf_str);
244 ats_free(m_agg_marshal_space);
245 ats_free(m_format_str);
246 m_valid = false;
247 }
248
249 /*-------------------------------------------------------------------------
250 LogFormat::format_from_specification
251
252 This routine is obsolete as of 3.1, but will be kept around to preserve
253 the old log config file option.
254
255 This (static) function examines the given log format specification string
256 and builds a new LogFormat object if the format specification is valid.
257 On success, a pointer to a LogFormat object allocated from the heap (with
258 new) will be returned. On error, NULL is returned.
259 -------------------------------------------------------------------------*/
260
261 LogFormat *
format_from_specification(char * spec,char ** file_name,char ** file_header,LogFileFormat * file_type)262 LogFormat::format_from_specification(char *spec, char **file_name, char **file_header, LogFileFormat *file_type)
263 {
264 LogFormat *format;
265 char *token;
266 int format_id;
267 char *format_name, *format_str;
268
269 ink_assert(file_name != nullptr);
270 ink_assert(file_header != nullptr);
271 ink_assert(file_type != nullptr);
272
273 SimpleTokenizer tok(spec, ':');
274
275 //
276 // Divide the specification string into tokens using the ':' as a
277 // field separator. There are currently eight (8) tokens that comprise
278 // a format specification. Verify each of the token values and if
279 // everything looks ok, then build the LogFormat object.
280 //
281 // First should be the "format" keyword that says this is a format spec.
282 //
283 token = tok.getNext();
284 if (token == nullptr) {
285 Debug("log-format", "token expected");
286 return nullptr;
287 }
288 if (strcasecmp(token, "format") == 0) {
289 Debug("log-format", "this is a format");
290 } else {
291 Debug("log-format", "should be 'format'");
292 return nullptr;
293 }
294
295 //
296 // Next should be the word "enabled" or "disabled", which indicates
297 // whether we should care about this format or not.
298 //
299 token = tok.getNext();
300 if (token == nullptr) {
301 Debug("log-format", "token expected");
302 return nullptr;
303 }
304 if (!strcasecmp(token, "disabled")) {
305 Debug("log-format", "format not enabled, skipping ...");
306 return nullptr;
307 } else if (!strcasecmp(token, "enabled")) {
308 Debug("log-format", "enabled format");
309 } else {
310 Debug("log-format", "should be 'enabled' or 'disabled', not %s", token);
311 return nullptr;
312 }
313
314 //
315 // Next should be the numeric format identifier
316 //
317 token = tok.getNext();
318 if (token == nullptr) {
319 Debug("log-format", "token expected");
320 return nullptr;
321 }
322 format_id = atoi(token);
323 // NOW UNUSED !!!
324
325 //
326 // Next should be the format name
327 //
328 token = tok.getNext();
329 if (token == nullptr) {
330 Debug("log-format", "token expected");
331 return nullptr;
332 }
333 format_name = token;
334
335 //
336 // Next should be the printf-stlye format symbol string
337 //
338 token = tok.getNext();
339 if (token == nullptr) {
340 Debug("log-format", "token expected");
341 return nullptr;
342 }
343 format_str = token;
344
345 //
346 // Next should be the file name for the log
347 //
348 token = tok.getNext();
349 if (token == nullptr) {
350 Debug("log-format", "token expected");
351 return nullptr;
352 }
353 *file_name = ats_strdup(token);
354
355 //
356 // Next should be the file type, either "ASCII" or "BINARY"
357 //
358 token = tok.getNext();
359 if (token == nullptr) {
360 Debug("log-format", "token expected");
361 return nullptr;
362 }
363 if (!strcasecmp(token, "ASCII")) {
364 *file_type = LOG_FILE_ASCII;
365 } else if (!strcasecmp(token, "BINARY")) {
366 *file_type = LOG_FILE_BINARY;
367 } else {
368 Debug("log-format", "%s is not a valid file format (ASCII or BINARY)", token);
369 return nullptr;
370 }
371
372 //
373 // the rest should be the file header
374 //
375 token = tok.getRest();
376 if (token == nullptr) {
377 Debug("log-format", "token expected");
378 return nullptr;
379 }
380 // set header to NULL if "none" was specified (a NULL header means
381 // "write no header" to the rest of the logging system)
382 //
383 *file_header = strcmp(token, "none") == 0 ? nullptr : ats_strdup(token);
384
385 Debug("log-format", "custom:%d:%s:%s:%s:%d:%s", format_id, format_name, format_str, *file_name, *file_type, token);
386
387 format = new LogFormat(format_name, format_str);
388 ink_assert(format != nullptr);
389 if (!format->valid()) {
390 delete format;
391 return nullptr;
392 }
393
394 return format;
395 }
396
397 /*-------------------------------------------------------------------------
398 LogFormat::parse_symbol_string
399
400 This function does the work of parsing a comma-separated symbol list and
401 adding them to the LogFieldList that is provided. The total number of
402 fields added to the list is returned.
403 -------------------------------------------------------------------------*/
404
405 int
parse_symbol_string(const char * symbol_string,LogFieldList * field_list,bool * contains_aggregates)406 LogFormat::parse_symbol_string(const char *symbol_string, LogFieldList *field_list, bool *contains_aggregates)
407 {
408 char *sym_str;
409 int field_count = 0;
410 LogField *f;
411 char *symbol, *name, *sym, *saveptr;
412 LogField::Container container;
413 LogField::Aggregate aggregate;
414
415 if (symbol_string == nullptr) {
416 return 0;
417 }
418 ink_assert(field_list != nullptr);
419 ink_assert(contains_aggregates != nullptr);
420
421 *contains_aggregates = false; // we'll change if it does
422
423 //
424 // strtok_r will mangle the input string; we'll make a copy for that.
425 //
426 sym_str = ats_strdup(symbol_string);
427 symbol = strtok_r(sym_str, ",", &saveptr);
428
429 while (symbol != nullptr) {
430 //
431 // See if there is an aggregate operator, which will contain "()"
432 //
433 char *begin_paren = strchr(symbol, '(');
434 if (begin_paren) {
435 char *end_paren = strchr(symbol, ')');
436 if (end_paren) {
437 Debug("log-agg", "Aggregate symbol: %s", symbol);
438 *begin_paren = '\0';
439 *end_paren = '\0';
440 name = begin_paren + 1;
441 sym = symbol;
442 Debug("log-agg", "Aggregate = %s, field = %s", sym, name);
443 aggregate = LogField::valid_aggregate_name(sym);
444 if (aggregate == LogField::NO_AGGREGATE) {
445 Note("Invalid aggregate specification: %s", sym);
446 } else {
447 if (aggregate == LogField::eCOUNT && strcmp(name, "*") == 0) {
448 f = Log::global_field_list.find_by_symbol("psql");
449 } else {
450 f = Log::global_field_list.find_by_symbol(name);
451 }
452 if (!f) {
453 Note("Invalid field symbol %s used in aggregate "
454 "operation",
455 name);
456 } else if (f->type() != LogField::sINT) {
457 Note("Only single integer field types may be aggregated");
458 } else {
459 LogField *new_f = new LogField(*f);
460 new_f->set_aggregate_op(aggregate);
461 field_list->add(new_f, false);
462 field_count++;
463 *contains_aggregates = true;
464 Debug("log-agg", "Aggregate field %s(%s) added", sym, name);
465 }
466 }
467 } else {
468 Note("Invalid aggregate field specification: no trailing "
469 "')' in %s",
470 symbol);
471 }
472 }
473 //
474 // Now check for a container field, which starts with '{'
475 //
476 else if (*symbol == '{') {
477 Debug("log-format", "Container symbol: %s", symbol);
478 f = nullptr;
479 char *name_end = strchr(symbol, '}');
480 if (name_end != nullptr) {
481 name = symbol + 1;
482 *name_end = 0; // changes '}' to '\0'
483 sym = name_end + 1; // start of container symbol
484 LogSlice slice(sym);
485 Debug("log-format", "Name = %s, symbol = %s", name, sym);
486 container = LogField::valid_container_name(sym);
487 if (container == LogField::NO_CONTAINER) {
488 Note("Invalid container specification: %s", sym);
489 } else {
490 f = new LogField(name, container);
491 ink_assert(f != nullptr);
492 if (slice.m_enable) {
493 f->m_slice = slice;
494 Debug("log-slice", "symbol = %s, [%d:%d]", sym, f->m_slice.m_start, f->m_slice.m_end);
495 }
496 field_list->add(f, false);
497 field_count++;
498 Debug("log-format", "Container field {%s}%s added", name, sym);
499 }
500 } else {
501 Note("Invalid container field specification: no trailing "
502 "'}' in %s",
503 symbol);
504 }
505 }
506 //
507 // treat this like a regular field symbol
508 //
509 else {
510 LogSlice slice(symbol);
511 Debug("log-format", "Regular field symbol: %s", symbol);
512 f = Log::global_field_list.find_by_symbol(symbol);
513 if (f != nullptr) {
514 LogField *cpy = new LogField(*f);
515 if (slice.m_enable) {
516 cpy->m_slice = slice;
517 Debug("log-slice", "symbol = %s, [%d:%d]", symbol, cpy->m_slice.m_start, cpy->m_slice.m_end);
518 }
519 field_list->add(cpy, false);
520 field_count++;
521 Debug("log-format", "Regular field %s added", symbol);
522 } else {
523 Note("The log format symbol %s was not found in the "
524 "list of known symbols.",
525 symbol);
526 field_list->addBadSymbol(symbol);
527 }
528 }
529
530 //
531 // Get the next symbol
532 //
533 symbol = strtok_r(nullptr, ",", &saveptr);
534 }
535
536 ats_free(sym_str);
537 return field_count;
538 }
539
540 //
541 // Parse escape string, supports two forms:
542 //
543 // 1) Octal representation: '\abc', for example: '\060'
544 // 0 < (a*8^2 + b*8 + c) < 255
545 //
546 // 2) Hex representation: '\xab', for example: '\x3A'
547 // 0 < (a*16 + b) < 255
548 //
549 // Return -1 if the beginning four characters are not valid
550 // escape sequence, otherwise return unsigned char value of the
551 // escape sequence in the string.
552 //
553 // NOTE: The value of escape sequence should be greater than 0
554 // and less than 255, since:
555 // - 0 is terminator of string, and
556 // - 255('\377') has been used as LOG_FIELD_MARKER.
557 //
558 int
parse_escape_string(const char * str,int len)559 LogFormat::parse_escape_string(const char *str, int len)
560 {
561 int sum, start = 0;
562 unsigned char a, b, c;
563
564 if (str[start] != '\\' || len < 2) {
565 return -1;
566 }
567
568 if (str[start + 1] == '\\') {
569 return '\\';
570 }
571 if (len < 4) {
572 return -1;
573 }
574
575 a = static_cast<unsigned char>(str[start + 1]);
576 b = static_cast<unsigned char>(str[start + 2]);
577 c = static_cast<unsigned char>(str[start + 3]);
578
579 if (isdigit(a) && isdigit(b)) {
580 sum = (a - '0') * 64 + (b - '0') * 8 + (c - '0');
581
582 if (sum == 0 || sum >= 255) {
583 Warning("Octal escape sequence out of range: \\%c%c%c, treat it as normal string\n", a, b, c);
584 return -1;
585 } else {
586 return sum;
587 }
588
589 } else if (tolower(a) == 'x' && isxdigit(b) && isxdigit(c)) {
590 int i, j;
591 if (isdigit(b)) {
592 i = b - '0';
593 } else {
594 i = toupper(b) - 'A' + 10;
595 }
596
597 if (isdigit(c)) {
598 j = c - '0';
599 } else {
600 j = toupper(c) - 'A' + 10;
601 }
602
603 sum = i * 16 + j;
604
605 if (sum == 0 || sum >= 255) {
606 Warning("Hex escape sequence out of range: \\%c%c%c, treat it as normal string\n", a, b, c);
607 return -1;
608 } else {
609 return sum;
610 }
611 }
612
613 return -1;
614 }
615
616 /*-------------------------------------------------------------------------
617 LogFormat::parse_format_string
618
619 This function will parse a custom log format string, which is a
620 combination of printf characters and logging field names, separating this
621 combined format string into a normal printf string and a fieldlist. The
622 number of logging fields parsed will be returned. The two strings
623 returned are allocated with ats_malloc, and should be released by the
624 caller. The function returns -1 on error.
625
626 For 3.1, I've added the ability to log summary information using
627 aggregate operators SUM, COUNT, AVG, ...
628 -------------------------------------------------------------------------*/
629
630 int
parse_format_string(const char * format_str,char ** printf_str,char ** fields_str)631 LogFormat::parse_format_string(const char *format_str, char **printf_str, char **fields_str)
632 {
633 ink_assert(printf_str != nullptr);
634 ink_assert(fields_str != nullptr);
635
636 if (format_str == nullptr) {
637 *printf_str = *fields_str = nullptr;
638 return 0;
639 }
640 //
641 // Since the given format string is a combination of the printf-string
642 // and the field symbols, when we break it up into these two components
643 // each is guaranteed to be smaller (or the same size) as the format
644 // string.
645 //
646 unsigned len = static_cast<unsigned>(::strlen(format_str));
647 *printf_str = static_cast<char *>(ats_malloc(len + 1));
648 *fields_str = static_cast<char *>(ats_malloc(len + 1));
649
650 unsigned printf_pos = 0;
651 unsigned fields_pos = 0;
652 unsigned field_count = 0;
653 unsigned field_len;
654 unsigned start, stop;
655 int escape_char;
656
657 for (start = 0; start < len; start++) {
658 //
659 // Look for logging fields: %<field>
660 //
661 if ((format_str[start] == '%') && (start + 1 < len) && (format_str[start + 1] == '<')) {
662 //
663 // this is a field symbol designation; look for the
664 // trailing '>'.
665 //
666 if (fields_pos > 0) {
667 (*fields_str)[fields_pos++] = ',';
668 }
669 for (stop = start + 2; stop < len; stop++) {
670 if (format_str[stop] == '>') {
671 break;
672 }
673 }
674 if (format_str[stop] == '>') {
675 //
676 // We found the termination for this field spec;
677 // copy the field symbol to the symbol string and place a
678 // LOG_FIELD_MARKER in the printf string.
679 //
680 field_len = stop - start - 2;
681 memcpy(&(*fields_str)[fields_pos], &format_str[start + 2], field_len);
682 fields_pos += field_len;
683 (*printf_str)[printf_pos++] = LOG_FIELD_MARKER;
684 ++field_count;
685 start = stop;
686 } else {
687 //
688 // This was not a logging field spec after all,
689 // then try to detect and parse escape string.
690 //
691 escape_char = parse_escape_string(&format_str[start], (len - start));
692
693 if (escape_char == '\\') {
694 start += 1;
695 (*printf_str)[printf_pos++] = static_cast<char>(escape_char);
696 } else if (escape_char >= 0) {
697 start += 3;
698 (*printf_str)[printf_pos++] = static_cast<char>(escape_char);
699 } else {
700 memcpy(&(*printf_str)[printf_pos], &format_str[start], stop - start + 1);
701 printf_pos += stop - start + 1;
702 }
703 }
704 } else {
705 //
706 // This was not the start of a logging field spec,
707 // then try to detect and parse escape string.
708 //
709 escape_char = parse_escape_string(&format_str[start], (len - start));
710
711 if (escape_char == '\\') {
712 start += 1;
713 (*printf_str)[printf_pos++] = static_cast<char>(escape_char);
714 } else if (escape_char >= 0) {
715 start += 3;
716 (*printf_str)[printf_pos++] = static_cast<char>(escape_char);
717 } else {
718 (*printf_str)[printf_pos++] = format_str[start];
719 }
720 }
721 }
722
723 //
724 // Ok, now NULL terminate the strings and return the number of fields
725 // actually found.
726 //
727 (*fields_str)[fields_pos] = '\0';
728 (*printf_str)[printf_pos] = '\0';
729
730 Debug("log-format", "LogFormat::parse_format_string: field_count=%d, \"%s\", \"%s\"", field_count, *fields_str, *printf_str);
731 return field_count;
732 }
733
734 /*-------------------------------------------------------------------------
735 LogFormat::display
736
737 Print out some info about this object.
738 -------------------------------------------------------------------------*/
739
740 void
display(FILE * fd)741 LogFormat::display(FILE *fd)
742 {
743 static const char *types[] = {"SQUID_LOG", "COMMON_LOG", "EXTENDED_LOG", "EXTENDED2_LOG", "LOG_FORMAT_CUSTOM", "LOG_FORMAT_TEXT"};
744
745 fprintf(fd, "--------------------------------------------------------\n");
746 fprintf(fd, "Format : %s (%s) (%p), %u fields.\n", m_name_str, types[m_format_type], this, m_field_count);
747 if (m_fieldlist_str) {
748 fprintf(fd, "Symbols: %s\n", m_fieldlist_str);
749 fprintf(fd, "Fields :\n");
750 m_field_list.display(fd);
751 } else {
752 fprintf(fd, "Fields : None\n");
753 }
754 fprintf(fd, "--------------------------------------------------------\n");
755 }
756
757 /*-------------------------------------------------------------------------
758 LogFormatList
759 -------------------------------------------------------------------------*/
760
761 LogFormatList::LogFormatList() = default;
762
~LogFormatList()763 LogFormatList::~LogFormatList()
764 {
765 clear();
766 }
767
768 void
clear()769 LogFormatList::clear()
770 {
771 LogFormat *f;
772 while ((f = m_format_list.dequeue())) {
773 delete f;
774 }
775 }
776
777 void
add(LogFormat * format,bool copy)778 LogFormatList::add(LogFormat *format, bool copy)
779 {
780 ink_assert(format != nullptr);
781
782 if (copy) {
783 m_format_list.enqueue(new LogFormat(*format));
784 } else {
785 m_format_list.enqueue(format);
786 }
787 }
788
789 LogFormat *
find_by_name(const char * name) const790 LogFormatList::find_by_name(const char *name) const
791 {
792 for (LogFormat *f = first(); f; f = next(f)) {
793 if (!strcmp(f->name(), name)) {
794 return f;
795 }
796 }
797 return nullptr;
798 }
799
800 unsigned
count()801 LogFormatList::count()
802 {
803 unsigned cnt = 0;
804 for (LogFormat *f = first(); f; f = next(f)) {
805 cnt++;
806 }
807 return cnt;
808 }
809
810 void
display(FILE * fd)811 LogFormatList::display(FILE *fd)
812 {
813 for (LogFormat *f = first(); f; f = next(f)) {
814 f->display(fd);
815 }
816 }
817