1 /** @file
2
3 This file implements the LogBuffer class, a thread-safe buffer for
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 @section description
24 This file implements the LogBuffer class, a thread-safe buffer for
25 recording log entries. See the header file LogBuffer.h for more
26 information on the structure of a LogBuffer.
27 */
28 #include "tscore/ink_platform.h"
29 #include "tscore/BufferWriter.h"
30
31 #include <cstdio>
32 #include <cstdlib>
33 #include <cstring>
34
35 #include "P_EventSystem.h"
36 #include "LogField.h"
37 #include "LogFilter.h"
38 #include "LogFormat.h"
39 #include "LogUtils.h"
40 #include "LogFile.h"
41 #include "LogObject.h"
42 #include "LogAccess.h"
43 #include "LogConfig.h"
44 #include "LogBuffer.h"
45 #include "Log.h"
46
47 struct FieldListCacheElement {
48 LogFieldList *fieldlist;
49 char *symbol_str;
50 };
51
52 enum {
53 FIELDLIST_CACHE_SIZE = 256,
54 };
55
56 FieldListCacheElement fieldlist_cache[FIELDLIST_CACHE_SIZE];
57 int fieldlist_cache_entries = 0;
58 int32_t LogBuffer::M_ID;
59
60 /*-------------------------------------------------------------------------
61 The following LogBufferHeader routines are used to grab strings out from
62 the data section using the offsets held in the buffer header.
63 -------------------------------------------------------------------------*/
64
65 char *
fmt_fieldlist()66 LogBufferHeader::fmt_fieldlist()
67 {
68 char *addr = nullptr;
69 if (fmt_fieldlist_offset) {
70 addr = reinterpret_cast<char *>(this) + fmt_fieldlist_offset;
71 }
72 return addr;
73 }
74
75 char *
fmt_printf()76 LogBufferHeader::fmt_printf()
77 {
78 char *addr = nullptr;
79 if (fmt_printf_offset) {
80 addr = reinterpret_cast<char *>(this) + fmt_printf_offset;
81 }
82 return addr;
83 }
84
85 char *
src_hostname()86 LogBufferHeader::src_hostname()
87 {
88 char *addr = nullptr;
89 if (src_hostname_offset) {
90 addr = reinterpret_cast<char *>(this) + src_hostname_offset;
91 }
92 return addr;
93 }
94
95 char *
log_filename()96 LogBufferHeader::log_filename()
97 {
98 char *addr = nullptr;
99 if (log_filename_offset) {
100 addr = reinterpret_cast<char *>(this) + log_filename_offset;
101 }
102 return addr;
103 }
104
LogBuffer(const LogConfig * cfg,LogObject * owner,size_t size,size_t buf_align,size_t write_align)105 LogBuffer::LogBuffer(const LogConfig *cfg, LogObject *owner, size_t size, size_t buf_align, size_t write_align)
106 : m_size(size), m_buf_align(buf_align), m_write_align(write_align), m_owner(owner), m_references(0)
107 {
108 size_t hdr_size;
109
110 // create the buffer
111 //
112 int64_t alloc_size = size + buf_align;
113
114 if (alloc_size <= BUFFER_SIZE_FOR_INDEX(cfg->logbuffer_max_iobuf_index)) {
115 m_buffer_fast_allocator_size = buffer_size_to_index(alloc_size, cfg->logbuffer_max_iobuf_index);
116 m_unaligned_buffer = static_cast<char *>(ioBufAllocator[m_buffer_fast_allocator_size].alloc_void());
117 } else {
118 m_buffer_fast_allocator_size = -1;
119 m_unaligned_buffer = static_cast<char *>(ats_malloc(alloc_size));
120 }
121 m_buffer = static_cast<char *>(align_pointer_forward(m_unaligned_buffer, buf_align));
122
123 // add the header
124 hdr_size = _add_buffer_header(cfg);
125
126 // initialize buffer state
127 m_state.s.offset = hdr_size;
128
129 // update the buffer id (m_id gets the old value)
130 m_id = static_cast<uint32_t>(ink_atomic_increment(&M_ID, 1));
131
132 m_expiration_time = LogUtils::timestamp() + cfg->max_secs_per_buffer;
133
134 Debug("log-logbuffer", "[%p] Created buffer %u for %s at address %p, size %d", this_ethread(), m_id, m_owner->get_base_filename(),
135 m_buffer, (int)size);
136 }
137
LogBuffer(LogObject * owner,LogBufferHeader * header)138 LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
139 : m_unaligned_buffer(nullptr),
140 m_buffer(reinterpret_cast<char *>(header)),
141 m_size(0),
142 m_buf_align(LB_DEFAULT_ALIGN),
143 m_write_align(INK_MIN_ALIGN),
144 m_buffer_fast_allocator_size(-1),
145 m_expiration_time(0),
146 m_owner(owner),
147 m_header(header),
148 m_references(0)
149 {
150 // This constructor does not allocate a buffer because it gets it as
151 // an argument. We set m_unaligned_buffer to NULL, which means that
152 // no checkout writes or checkin writes are allowed. This is enforced
153 // by the asserts in checkout_write and checkin_write
154
155 // update the buffer id (m_id gets the old value)
156 //
157 m_id = static_cast<uint32_t>(ink_atomic_increment(&M_ID, 1));
158
159 Debug("log-logbuffer", "[%p] Created repurposed buffer %u for %s at address %p", this_ethread(), m_id,
160 m_owner->get_base_filename(), m_buffer);
161 }
162
163 void
freeLogBuffer()164 LogBuffer::freeLogBuffer()
165 {
166 char *log_buffer = nullptr;
167
168 if (m_unaligned_buffer) {
169 log_buffer = m_unaligned_buffer;
170 } else {
171 log_buffer = m_buffer;
172 }
173 if (log_buffer) {
174 Debug("log-logbuffer", "[%p] Deleting buffer %u at address %p", this_ethread(), m_id, log_buffer);
175 if (m_buffer_fast_allocator_size >= 0) {
176 ioBufAllocator[m_buffer_fast_allocator_size].free_void(log_buffer);
177 } else {
178 ats_free(log_buffer);
179 }
180 }
181 }
182
~LogBuffer()183 LogBuffer::~LogBuffer()
184 {
185 freeLogBuffer();
186 m_buffer = nullptr;
187 m_unaligned_buffer = nullptr;
188 }
189
190 /*-------------------------------------------------------------------------
191 LogBuffer::checkout_write
192 -------------------------------------------------------------------------*/
193
194 LogBuffer::LB_ResultCode
checkout_write(size_t * write_offset,size_t write_size)195 LogBuffer::checkout_write(size_t *write_offset, size_t write_size)
196 {
197 // checkout_write should not be called if m_unaligned_buffer was
198 // not allocated, which means that the actual buffer data was given
199 // to the buffer upon construction. Or, in other words, that
200 // LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
201 // was used to construct the object
202 //
203 ink_assert(m_unaligned_buffer);
204
205 LB_ResultCode ret_val = LB_BUSY;
206 LB_State old_s, new_s;
207 size_t offset = 0;
208 size_t actual_write_size = INK_ALIGN(write_size + sizeof(LogEntryHeader), m_write_align);
209
210 uint64_t retries = static_cast<uint64_t>(-1);
211 do {
212 // we want sequence points between these two statements
213 old_s = m_state;
214 new_s = old_s;
215
216 if (old_s.s.full) {
217 // the buffer has already been set to full by somebody else
218 // just tell the caller to retry
219 ret_val = LB_RETRY;
220 break;
221 } else {
222 // determine what the state would be if nobody changes it
223 // before we do
224
225 if (write_offset) {
226 if (old_s.s.offset + actual_write_size <= m_size) {
227 // there is room for this entry, update the state
228
229 offset = old_s.s.offset;
230
231 ++new_s.s.num_writers;
232 new_s.s.offset += actual_write_size;
233 ++new_s.s.num_entries;
234
235 ret_val = LB_OK;
236 } else {
237 // there is no room for this entry
238
239 if (old_s.s.num_entries == 0) {
240 ret_val = LB_BUFFER_TOO_SMALL;
241 } else {
242 new_s.s.full = 1;
243 ret_val = old_s.s.num_writers ? LB_FULL_ACTIVE_WRITERS : LB_FULL_NO_WRITERS;
244 }
245 }
246 } else {
247 // this is a request to set the buffer as full
248 // (write_offset == NULL)
249
250 if (old_s.s.num_entries) {
251 new_s.s.full = 1;
252
253 ret_val = (old_s.s.num_writers ? LB_FULL_ACTIVE_WRITERS : LB_FULL_NO_WRITERS);
254 } else {
255 // the buffer has no entries, do nothing
256
257 ret_val = LB_OK;
258 break;
259 }
260 }
261
262 if (switch_state(old_s, new_s)) {
263 // we succeeded in setting the new state
264 break;
265 }
266 }
267 ret_val = LB_BUSY;
268 } while (--retries);
269
270 // add the entry header to the buffer if this was a real checkout and
271 // the checkout was successful
272 //
273 if (write_offset && ret_val == LB_OK) {
274 // disable statistics for now
275 // ProxyMutex *mutex = this_ethread()->mutex;
276 // ink_release_assert(mutex->thread_holding == this_ethread());
277 // SUM_DYN_STAT(log_stat_bytes_buffered_stat, actual_write_size);
278
279 LogEntryHeader *entry_header = reinterpret_cast<LogEntryHeader *>(&m_buffer[offset]);
280 // entry_header->timestamp = LogUtils::timestamp();
281 struct timeval tp = ink_gettimeofday();
282
283 entry_header->timestamp = tp.tv_sec;
284 entry_header->timestamp_usec = tp.tv_usec;
285 entry_header->entry_len = actual_write_size;
286
287 *write_offset = offset + sizeof(LogEntryHeader);
288 }
289 // Debug("log-logbuffer","[%p] %s for buffer %u (%s) returning %d",
290 // this_ethread(),
291 // (write_offset ? "checkout_write" : "force_new_buffer"),
292 // m_id, m_owner->get_base_filename(), ret_val);
293
294 return ret_val;
295 }
296
297 /*-------------------------------------------------------------------------
298 LogBuffer::checkin_write
299 -------------------------------------------------------------------------*/
300
301 LogBuffer::LB_ResultCode
checkin_write(size_t write_offset)302 LogBuffer::checkin_write(size_t write_offset)
303 {
304 // checkin_write should not be called if m_unaligned_buffer was
305 // not allocated, which means that the actual buffer data was given
306 // to the buffer upon construction. Or, in other words, that
307 // LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
308 // was used to construct the object
309 //
310 ink_assert(m_unaligned_buffer);
311
312 LB_ResultCode ret_val = LB_OK;
313 LB_State old_s, new_s;
314
315 do {
316 new_s = old_s = m_state;
317
318 ink_assert(write_offset < old_s.s.offset);
319 ink_assert(old_s.s.num_writers > 0);
320
321 if (--new_s.s.num_writers == 0) {
322 ret_val = (old_s.s.full ? LB_ALL_WRITERS_DONE : LB_OK);
323 }
324
325 } while (!switch_state(old_s, new_s));
326
327 // Debug("log-logbuffer","[%p] checkin_write for buffer %u (%s) "
328 // "returning %d (%u writers left)", this_ethread(),
329 // m_id, m_owner->get_base_filename(), ret_val, writers_left);
330
331 return ret_val;
332 }
333
334 unsigned
add_header_str(const char * str,char * buf_ptr,unsigned buf_len)335 LogBuffer::add_header_str(const char *str, char *buf_ptr, unsigned buf_len)
336 {
337 unsigned len = 0;
338 // This was ambiguous - should it be the real strlen or the
339 // rounded up value? Given the +1, presumably it's the real length
340 // plus the terminating nul.
341 if (likely(str && (len = (unsigned)(::strlen(str) + 1)) < buf_len)) {
342 ink_strlcpy(buf_ptr, str, buf_len);
343 }
344 return len;
345 }
346
347 size_t
_add_buffer_header(const LogConfig * cfg)348 LogBuffer::_add_buffer_header(const LogConfig *cfg)
349 {
350 size_t header_len;
351
352 //
353 // initialize the header
354 //
355 LogFormat *fmt = m_owner->m_format;
356 m_header = reinterpret_cast<LogBufferHeader *>(m_buffer);
357 m_header->cookie = LOG_SEGMENT_COOKIE;
358 m_header->version = LOG_SEGMENT_VERSION;
359 m_header->format_type = fmt->type();
360 m_header->entry_count = 0;
361 m_header->low_timestamp = LogUtils::timestamp();
362 m_header->high_timestamp = 0;
363 m_header->log_object_signature = m_owner->get_signature();
364 m_header->log_object_flags = m_owner->get_flags();
365 #if defined(LOG_BUFFER_TRACKING)
366 m_header->id = lrand48();
367 #endif // defined(LOG_BUFFER_TRACKING)
368
369 //
370 // The remaining header fields actually point into the data section of
371 // the buffer. Write the data into the buffer and update the total
372 // size of the buffer header.
373 //
374
375 header_len = sizeof(LogBufferHeader); // at least ...
376
377 m_header->fmt_name_offset = 0;
378 m_header->fmt_fieldlist_offset = 0;
379 m_header->fmt_printf_offset = 0;
380 m_header->src_hostname_offset = 0;
381 m_header->log_filename_offset = 0;
382
383 if (fmt->name()) {
384 m_header->fmt_name_offset = header_len;
385 header_len += add_header_str(fmt->name(), &m_buffer[header_len], m_size - header_len);
386 }
387 if (fmt->fieldlist()) {
388 m_header->fmt_fieldlist_offset = header_len;
389 header_len += add_header_str(fmt->fieldlist(), &m_buffer[header_len], m_size - header_len);
390 }
391 if (fmt->printf_str()) {
392 m_header->fmt_printf_offset = header_len;
393 header_len += add_header_str(fmt->printf_str(), &m_buffer[header_len], m_size - header_len);
394 }
395 if (cfg->hostname) {
396 m_header->src_hostname_offset = header_len;
397 header_len += add_header_str(cfg->hostname, &m_buffer[header_len], m_size - header_len);
398 }
399 if (m_owner->get_base_filename()) {
400 m_header->log_filename_offset = header_len;
401 header_len += add_header_str(m_owner->get_base_filename(), &m_buffer[header_len], m_size - header_len);
402 }
403 // update the rest of the header fields; make sure the header_len is
404 // correctly aligned, so that the first record will start on a legal
405 // alignment mark.
406 //
407
408 header_len = INK_ALIGN_DEFAULT(header_len);
409
410 m_header->byte_count = header_len;
411 m_header->data_offset = header_len;
412
413 return header_len;
414 }
415
416 void
update_header_data()417 LogBuffer::update_header_data()
418 {
419 // only update the header if the LogBuffer did not receive its data
420 // upon construction (i.e., if m_unaligned_buffer was allocated)
421 //
422
423 if (m_unaligned_buffer) {
424 m_header->entry_count = m_state.s.num_entries;
425 m_header->byte_count = m_state.s.offset;
426 m_header->high_timestamp = LogUtils::timestamp();
427 }
428 }
429
430 /*-------------------------------------------------------------------------
431 LogBuffer::max_entry_bytes
432
433 This static function simply returns the greatest number of bytes than an
434 entry can be and fit into a LogBuffer.
435 -------------------------------------------------------------------------*/
436 size_t
max_entry_bytes()437 LogBuffer::max_entry_bytes()
438 {
439 return (Log::config->log_buffer_size - sizeof(LogBufferHeader));
440 }
441
442 /*-------------------------------------------------------------------------
443 LogBuffer::resolve_custom_entry
444 -------------------------------------------------------------------------*/
445 int
resolve_custom_entry(LogFieldList * fieldlist,char * printf_str,char * read_from,char * write_to,int write_to_len,long timestamp,long timestamp_usec,unsigned buffer_version,LogFieldList * alt_fieldlist,char * alt_printf_str)446 LogBuffer::resolve_custom_entry(LogFieldList *fieldlist, char *printf_str, char *read_from, char *write_to, int write_to_len,
447 long timestamp, long timestamp_usec, unsigned buffer_version, LogFieldList *alt_fieldlist,
448 char *alt_printf_str)
449 {
450 if (fieldlist == nullptr || printf_str == nullptr) {
451 return 0;
452 }
453
454 int *readfrom_map = nullptr;
455
456 if (alt_fieldlist && alt_printf_str) {
457 LogField *f, *g;
458 int n_alt_fields = alt_fieldlist->count();
459 int i = 0;
460
461 readfrom_map = static_cast<int *>(ats_malloc(n_alt_fields * sizeof(int)));
462 for (f = alt_fieldlist->first(); f; f = alt_fieldlist->next(f)) {
463 int readfrom_pos = 0;
464 bool found_match = false;
465 for (g = fieldlist->first(); g; g = fieldlist->next(g)) {
466 if (strcmp(f->symbol(), g->symbol()) == 0) {
467 found_match = true;
468 readfrom_map[i++] = readfrom_pos;
469 break;
470 }
471 // TODO handle readfrom_pos properly
472 // readfrom_pos += g->size();
473 }
474 if (!found_match) {
475 Note("Alternate format contains a field (%s) not in the "
476 "format logged",
477 f->symbol());
478 break;
479 }
480 }
481 }
482 //
483 // Loop over the printf_str, copying everything to the write_to buffer
484 // except the LOG_FIELD_MARKER characters. When we reach those, we
485 // substitute the string from the unmarshal routine of the current
486 // LogField object, obtained from the fieldlist.
487 //
488
489 LogField *field = fieldlist->first();
490 LogField *lastField = nullptr; // For debug message.
491 int markCount = 0; // For debug message.
492 int printf_len = static_cast<int>(::strlen(printf_str)); // OPTIMIZE
493 int bytes_written = 0;
494 int res, i;
495
496 const char *buffer_size_exceeded_msg = "Traffic Server is skipping the current log entry because its size "
497 "exceeds the maximum line (entry) size for an ascii log buffer";
498
499 for (i = 0; i < printf_len; i++) {
500 if (printf_str[i] == LOG_FIELD_MARKER) {
501 ++markCount;
502 if (field != nullptr) {
503 char *to = &write_to[bytes_written];
504 res = field->unmarshal(&read_from, to, write_to_len - bytes_written);
505
506 if (res < 0) {
507 Note("%s", buffer_size_exceeded_msg);
508 bytes_written = 0;
509 break;
510 }
511
512 bytes_written += res;
513 lastField = field;
514 field = fieldlist->next(field);
515 } else {
516 ts::LocalBufferWriter<10 * 1024> bw;
517 if (auto bs = fieldlist->badSymbols(); bs.size() > 0) {
518 bw << " (likely due to bad symbols " << bs << " in log format)";
519 }
520 Note("There are more field markers than fields%*s;"
521 " cannot process log entry '%.*s'. Last field = '%s' printf_str='%s' pos=%d/%d count=%d alt_printf_str='%s'",
522 static_cast<int>(bw.size()), bw.data(), bytes_written, write_to, lastField == nullptr ? "*" : lastField->symbol(),
523 printf_str == nullptr ? "*NULL*" : printf_str, i, printf_len, markCount,
524 alt_printf_str == nullptr ? "*NULL*" : alt_printf_str);
525 bytes_written = 0;
526 break;
527 }
528 } else {
529 if (1 + bytes_written < write_to_len) {
530 write_to[bytes_written++] = printf_str[i];
531 } else {
532 Note("%s", buffer_size_exceeded_msg);
533 bytes_written = 0;
534 break;
535 }
536 }
537 }
538
539 ats_free(readfrom_map);
540 return bytes_written;
541 }
542
543 /*-------------------------------------------------------------------------
544 LogBuffer::to_ascii
545
546 This routine converts a log entry into an ascii string in the buffer
547 space provided, and returns the length of the new string (not including
548 trailing null, like strlen).
549 -------------------------------------------------------------------------*/
550 int
to_ascii(LogEntryHeader * entry,LogFormatType type,char * buf,int buf_len,const char * symbol_str,char * printf_str,unsigned buffer_version,const char * alt_format)551 LogBuffer::to_ascii(LogEntryHeader *entry, LogFormatType type, char *buf, int buf_len, const char *symbol_str, char *printf_str,
552 unsigned buffer_version, const char *alt_format)
553 {
554 ink_assert(entry != nullptr);
555 ink_assert(type == LOG_FORMAT_CUSTOM || type == LOG_FORMAT_TEXT);
556 ink_assert(buf != nullptr);
557
558 char *read_from; // keeps track of where we're reading from entry
559 char *write_to; // keeps track of where we're writing into buf
560
561 read_from = reinterpret_cast<char *>(entry) + sizeof(LogEntryHeader);
562 write_to = buf;
563
564 if (type == LOG_FORMAT_TEXT) {
565 //
566 // text log entries are just strings, so simply move it into the
567 // format buffer.
568 //
569 return ink_strlcpy(write_to, read_from, buf_len);
570 }
571 //
572 // We no longer make the distinction between custom vs pre-defined
573 // logging formats in converting to ASCII. This way we're sure to
574 // always be using the correct printf string and symbols for this
575 // buffer since we get it from the buffer header.
576 //
577 // We want to cache the unmarshaling "plans" so that we don't have to
578 // re-create them each time. We can use the symbol string as a key to
579 // these stored plans.
580 //
581
582 int i;
583 LogFieldList *fieldlist = nullptr;
584 bool delete_fieldlist_p = false; // need to free the fieldlist?
585
586 for (i = 0; i < fieldlist_cache_entries; i++) {
587 if (strcmp(symbol_str, fieldlist_cache[i].symbol_str) == 0) {
588 Debug("log-fieldlist", "Fieldlist for %s found in cache, #%d", symbol_str, i);
589 fieldlist = fieldlist_cache[i].fieldlist;
590 break;
591 }
592 }
593
594 if (!fieldlist) {
595 Debug("log-fieldlist", "Fieldlist for %s not found; creating ...", symbol_str);
596 fieldlist = new LogFieldList;
597 ink_assert(fieldlist != nullptr);
598 bool contains_aggregates = false;
599 LogFormat::parse_symbol_string(symbol_str, fieldlist, &contains_aggregates);
600
601 if (fieldlist_cache_entries < FIELDLIST_CACHE_SIZE) {
602 Debug("log-fieldlist", "Fieldlist cached as entry %d", fieldlist_cache_entries);
603 fieldlist_cache[fieldlist_cache_entries].fieldlist = fieldlist;
604 fieldlist_cache[fieldlist_cache_entries].symbol_str = ats_strdup(symbol_str);
605 fieldlist_cache_entries++;
606 } else {
607 delete_fieldlist_p = true;
608 }
609 }
610
611 LogFieldList *alt_fieldlist = nullptr;
612 char *alt_printf_str = nullptr;
613 char *alt_symbol_str = nullptr;
614 bool bad_alt_format = false;
615
616 if (alt_format) {
617 int n_alt_fields = LogFormat::parse_format_string(alt_format, &alt_printf_str, &alt_symbol_str);
618 if (n_alt_fields < 0) {
619 Note("Error parsing alternate format string: %s", alt_format);
620 bad_alt_format = true;
621 }
622
623 if (!bad_alt_format) {
624 alt_fieldlist = new LogFieldList;
625 bool contains_aggs = false;
626 int n_alt_fields2 = LogFormat::parse_symbol_string(alt_symbol_str, alt_fieldlist, &contains_aggs);
627 if (n_alt_fields2 > 0 && contains_aggs) {
628 Note("Alternative formats not allowed to contain aggregates");
629 bad_alt_format = true;
630 ;
631 }
632 }
633 }
634
635 if (bad_alt_format) {
636 delete alt_fieldlist;
637 ats_free(alt_printf_str);
638 ats_free(alt_symbol_str);
639 alt_fieldlist = nullptr;
640 alt_printf_str = nullptr;
641 alt_symbol_str = nullptr;
642 }
643
644 int ret = resolve_custom_entry(fieldlist, printf_str, read_from, write_to, buf_len, entry->timestamp, entry->timestamp_usec,
645 buffer_version, alt_fieldlist, alt_printf_str);
646
647 delete alt_fieldlist;
648 ats_free(alt_printf_str);
649 ats_free(alt_symbol_str);
650 if (delete_fieldlist_p) {
651 delete fieldlist;
652 }
653
654 return ret;
655 }
656
657 /*-------------------------------------------------------------------------
658 LogBufferList
659
660 The operations on this list need to be atomic because the buffers are
661 added by client threads and removed by the logging thread. This is
662 accomplished by protecting the operations with a mutex.
663
664 Also, the list must offer FIFO semantics so that a buffers are removed
665 from the list in the same order that they are added, so that timestamp
666 ordering in the log file is preserved.
667 -------------------------------------------------------------------------*/
668
669 /*-------------------------------------------------------------------------
670 LogBufferList::LogBufferList
671 -------------------------------------------------------------------------*/
672
LogBufferList()673 LogBufferList::LogBufferList()
674 {
675 m_size = 0;
676 ink_mutex_init(&m_mutex);
677 }
678
679 /*-------------------------------------------------------------------------
680 LogBufferList::~LogBufferList
681 -------------------------------------------------------------------------*/
682
~LogBufferList()683 LogBufferList::~LogBufferList()
684 {
685 LogBuffer *lb;
686 while ((lb = get()) != nullptr) {
687 delete lb;
688 }
689 m_size = 0;
690 ink_mutex_destroy(&m_mutex);
691 }
692
693 /*-------------------------------------------------------------------------
694 LogBufferList::add (or enqueue)
695 -------------------------------------------------------------------------*/
696
697 void
add(LogBuffer * lb)698 LogBufferList::add(LogBuffer *lb)
699 {
700 ink_assert(lb != nullptr);
701
702 ink_mutex_acquire(&m_mutex);
703 m_buffer_list.enqueue(lb);
704 ink_assert(m_size >= 0);
705 m_size++;
706 ink_mutex_release(&m_mutex);
707 }
708
709 /*-------------------------------------------------------------------------
710 LogBufferList::get (or dequeue)
711 -------------------------------------------------------------------------*/
712
713 LogBuffer *
get()714 LogBufferList::get()
715 {
716 LogBuffer *lb;
717
718 ink_mutex_acquire(&m_mutex);
719 lb = m_buffer_list.dequeue();
720 if (lb != nullptr) {
721 m_size--;
722 ink_assert(m_size >= 0);
723 }
724 ink_mutex_release(&m_mutex);
725 return lb;
726 }
727
728 /*-------------------------------------------------------------------------
729 -------------------------------------------------------------------------*/
730
731 LogEntryHeader *
next()732 LogBufferIterator::next()
733 {
734 LogEntryHeader *ret_val = nullptr;
735 LogEntryHeader *entry = reinterpret_cast<LogEntryHeader *>(m_next);
736
737 if (entry) {
738 if (m_iter_entry_count < m_buffer_entry_count) {
739 m_next += entry->entry_len;
740 ++m_iter_entry_count;
741 ret_val = entry;
742 }
743 }
744
745 return ret_val;
746 }
747