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 LogObject.cc
26
27
28 ***************************************************************************/
29 #include "tscore/ink_platform.h"
30 #include "tscore/CryptoHash.h"
31 #include "P_EventSystem.h"
32 #include "LogUtils.h"
33 #include "LogField.h"
34 #include "LogObject.h"
35 #include "LogConfig.h"
36 #include "LogAccess.h"
37 #include "Log.h"
38 #include "tscore/TestBox.h"
39
40 #include <algorithm>
41 #include <vector>
42 #include <thread>
43
44 static bool
should_roll_on_time(Log::RollingEnabledValues roll)45 should_roll_on_time(Log::RollingEnabledValues roll)
46 {
47 return roll == Log::ROLL_ON_TIME_ONLY || roll == Log::ROLL_ON_TIME_OR_SIZE;
48 }
49
50 static bool
should_roll_on_size(Log::RollingEnabledValues roll)51 should_roll_on_size(Log::RollingEnabledValues roll)
52 {
53 return roll == Log::ROLL_ON_SIZE_ONLY || roll == Log::ROLL_ON_TIME_OR_SIZE;
54 }
55
56 size_t
preproc_buffers(LogBufferSink * sink)57 LogBufferManager::preproc_buffers(LogBufferSink *sink)
58 {
59 SList(LogBuffer, write_link) q(write_list.popall()), new_q;
60 LogBuffer *b = nullptr;
61 while ((b = q.pop())) {
62 if (b->m_references || b->m_state.s.num_writers) {
63 // Still has outstanding references.
64 write_list.push(b);
65 } else if (_num_flush_buffers > FLUSH_ARRAY_SIZE) {
66 ink_atomic_increment(&_num_flush_buffers, -1);
67 Warning("Dropping log buffer, can't keep up.");
68 RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_bytes_lost_before_preproc_stat,
69 b->header()->byte_count);
70 delete b;
71 } else {
72 new_q.push(b);
73 }
74 }
75
76 int prepared = 0;
77 while ((b = new_q.pop())) {
78 b->update_header_data();
79 sink->preproc_and_try_delete(b);
80 ink_atomic_increment(&_num_flush_buffers, -1);
81 prepared++;
82 }
83
84 Debug("log-logbuffer", "prepared %d buffers", prepared);
85 return prepared;
86 }
87
88 /*-------------------------------------------------------------------------
89 LogObject
90 -------------------------------------------------------------------------*/
91
LogObject(LogConfig * cfg,const LogFormat * format,const char * log_dir,const char * basename,LogFileFormat file_format,const char * header,Log::RollingEnabledValues rolling_enabled,int flush_threads,int rolling_interval_sec,int rolling_offset_hr,int rolling_size_mb,bool auto_created,int rolling_max_count,int rolling_min_count,bool reopen_after_rolling,int pipe_buffer_size)92 LogObject::LogObject(LogConfig *cfg, const LogFormat *format, const char *log_dir, const char *basename, LogFileFormat file_format,
93 const char *header, Log::RollingEnabledValues rolling_enabled, int flush_threads, int rolling_interval_sec,
94 int rolling_offset_hr, int rolling_size_mb, bool auto_created, int rolling_max_count, int rolling_min_count,
95 bool reopen_after_rolling, int pipe_buffer_size)
96 : m_alt_filename(nullptr),
97 m_flags(0),
98 m_signature(0),
99 m_flush_threads(flush_threads),
100 m_rolling_interval_sec(rolling_interval_sec),
101 m_rolling_offset_hr(rolling_offset_hr),
102 m_rolling_size_mb(rolling_size_mb),
103 m_last_roll_time(0),
104 m_max_rolled(rolling_max_count),
105 m_min_rolled(rolling_min_count),
106 m_reopen_after_rolling(reopen_after_rolling),
107 m_buffer_manager_idx(0),
108 m_pipe_buffer_size(pipe_buffer_size)
109 {
110 ink_release_assert(format);
111 m_format = new LogFormat(*format);
112 m_buffer_manager = new LogBufferManager[m_flush_threads];
113
114 if (file_format == LOG_FILE_BINARY) {
115 m_flags |= BINARY;
116 } else if (file_format == LOG_FILE_PIPE) {
117 m_flags |= WRITES_TO_PIPE;
118 }
119
120 generate_filenames(log_dir, basename, file_format);
121
122 // compute_signature is a static function
123 m_signature = compute_signature(m_format, m_basename, m_flags);
124
125 m_logFile =
126 new LogFile(m_filename, header, file_format, m_signature, cfg->ascii_buffer_size, cfg->max_line_size, m_pipe_buffer_size);
127
128 if (m_reopen_after_rolling) {
129 m_logFile->open_file();
130 }
131
132 LogBuffer *b = new LogBuffer(cfg, this, cfg->log_buffer_size);
133 ink_assert(b);
134 SET_FREELIST_POINTER_VERSION(m_log_buffer, b, 0);
135
136 _setup_rolling(cfg, rolling_enabled, rolling_interval_sec, rolling_offset_hr, rolling_size_mb);
137
138 Debug("log-config", "exiting LogObject constructor, filename=%s this=%p", m_filename, this);
139 }
140
~LogObject()141 LogObject::~LogObject()
142 {
143 Debug("log-config", "entering LogObject destructor, this=%p", this);
144
145 preproc_buffers();
146 ats_free(m_basename);
147 ats_free(m_filename);
148 ats_free(m_alt_filename);
149 delete m_format;
150 delete[] m_buffer_manager;
151 delete static_cast<LogBuffer *>(FREELIST_POINTER(m_log_buffer));
152 }
153
154 //-----------------------------------------------------------------------------
155 //
156 // This function generates an object filename according to the following rules:
157 //
158 // 1.- if no extension is given, add .log for ascii logs, and .blog for
159 // binary logs
160 // 2.- if an extension is given, then do not modify filename and use that
161 // extension regardless of type of log
162 // 3.- if there is a '.' at the end of the name, then do not add an extension
163 // and remove the '.'. To have a dot at the end of the filename, specify
164 // two ('..').
165 //
166 void
generate_filenames(const char * log_dir,const char * basename,LogFileFormat file_format)167 LogObject::generate_filenames(const char *log_dir, const char *basename, LogFileFormat file_format)
168 {
169 ink_assert(log_dir && basename);
170
171 int i = -1, len = 0;
172 char c;
173 while (c = basename[len], c != 0) {
174 if (c == '.') {
175 i = len;
176 }
177 ++len;
178 }
179 if (i == len - 1) {
180 --len;
181 }; // remove dot at end of name
182
183 const char *ext = nullptr;
184 int ext_len = 0;
185 if (i < 0) { // no extension, add one
186 switch (file_format) {
187 case LOG_FILE_ASCII:
188 ext = LOG_FILE_ASCII_OBJECT_FILENAME_EXTENSION;
189 ext_len = 4;
190 break;
191 case LOG_FILE_BINARY:
192 ext = LOG_FILE_BINARY_OBJECT_FILENAME_EXTENSION;
193 ext_len = 5;
194 break;
195 case LOG_FILE_PIPE:
196 ext = LOG_FILE_PIPE_OBJECT_FILENAME_EXTENSION;
197 ext_len = 5;
198 break;
199 default:
200 ink_assert(!"unknown file format");
201 }
202 }
203
204 int dir_len = static_cast<int>(strlen(log_dir));
205 int basename_len = len + ext_len + 1; // include null terminator
206 int total_len = dir_len + 1 + basename_len; // include '/'
207
208 m_filename = static_cast<char *>(ats_malloc(total_len));
209 m_basename = static_cast<char *>(ats_malloc(basename_len));
210
211 memcpy(m_filename, log_dir, dir_len);
212 m_filename[dir_len++] = '/';
213 memcpy(&m_filename[dir_len], basename, len);
214 memcpy(m_basename, basename, len);
215
216 if (ext_len) {
217 memcpy(&m_filename[dir_len + len], ext, ext_len);
218 memcpy(&m_basename[len], ext, ext_len);
219 }
220 m_filename[total_len - 1] = 0;
221 m_basename[basename_len - 1] = 0;
222 }
223
224 void
rename(char * new_name)225 LogObject::rename(char *new_name)
226 {
227 // NOTE: this function is intended to be called by the LogObjectManager
228 // while solving filename conflicts. It DOES NOT modify the signature of
229 // the LogObject to match the new filename.
230 //
231 ats_free(m_alt_filename);
232 m_alt_filename = ats_strdup(new_name);
233 m_logFile->change_name(new_name);
234 }
235
236 void
add_filter(LogFilter * filter,bool copy)237 LogObject::add_filter(LogFilter *filter, bool copy)
238 {
239 if (!filter) {
240 return;
241 }
242 m_filter_list.add(filter, copy);
243 }
244
245 void
set_filter_list(const LogFilterList & list,bool copy)246 LogObject::set_filter_list(const LogFilterList &list, bool copy)
247 {
248 LogFilter *f;
249
250 m_filter_list.clear();
251 for (f = list.first(); f != nullptr; f = list.next(f)) {
252 m_filter_list.add(f, copy);
253 }
254 m_filter_list.set_conjunction(list.does_conjunction());
255 }
256
257 // we compute the object signature from the fieldlist_str and the printf_str
258 // of the LogFormat rather than from the format_str because the format_str
259 // is not part of a LogBuffer header
260 //
261 uint64_t
compute_signature(LogFormat * format,char * filename,unsigned int flags)262 LogObject::compute_signature(LogFormat *format, char *filename, unsigned int flags)
263 {
264 char *fl = format->fieldlist();
265 char *ps = format->printf_str();
266 uint64_t signature = 0;
267
268 if (fl && ps && filename) {
269 int buf_size = strlen(fl) + strlen(ps) + strlen(filename) + 2;
270 char *buffer = static_cast<char *>(ats_malloc(buf_size));
271
272 ink_string_concatenate_strings(buffer, fl, ps, filename,
273 flags & LogObject::BINARY ? "B" : (flags & LogObject::WRITES_TO_PIPE ? "P" : "A"), NULL);
274
275 CryptoHash hash;
276 CryptoContext().hash_immediate(hash, buffer, buf_size - 1);
277 signature = hash.fold();
278
279 ats_free(buffer);
280 }
281 return signature;
282 }
283
284 void
display(FILE * fd)285 LogObject::display(FILE *fd)
286 {
287 fprintf(fd, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
288 fprintf(fd,
289 "LogObject [%p]: format = %s (%p)\nbasename = %s\n"
290 "flags = %u\n"
291 "signature = %" PRIu64 "\n",
292 this, m_format->name(), m_format, m_basename, m_flags, m_signature);
293
294 fprintf(fd, "full path = %s\n", get_full_filename());
295 m_filter_list.display(fd);
296 fprintf(fd, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
297 }
298
299 static head_p
increment_pointer_version(head_p * dst)300 increment_pointer_version(head_p *dst)
301 {
302 head_p h;
303 head_p new_h;
304
305 do {
306 INK_QUEUE_LD(h, *dst);
307 SET_FREELIST_POINTER_VERSION(new_h, FREELIST_POINTER(h), FREELIST_VERSION(h) + 1);
308 } while (ink_atomic_cas(&dst->data, h.data, new_h.data) == false);
309
310 return h;
311 }
312
313 static bool
write_pointer_version(head_p * dst,head_p old_h,void * ptr,head_p::version_type vers)314 write_pointer_version(head_p *dst, head_p old_h, void *ptr, head_p::version_type vers)
315 {
316 head_p tmp_h;
317
318 SET_FREELIST_POINTER_VERSION(tmp_h, ptr, vers);
319 return ink_atomic_cas(&dst->data, old_h.data, tmp_h.data);
320 }
321
322 LogBuffer *
_checkout_write(size_t * write_offset,size_t bytes_needed)323 LogObject::_checkout_write(size_t *write_offset, size_t bytes_needed)
324 {
325 LogBuffer::LB_ResultCode result_code;
326 LogBuffer *buffer;
327 LogBuffer *new_buffer = nullptr;
328 bool retry = true;
329 head_p old_h;
330
331 do {
332 // To avoid a race condition, we keep a count of held references in
333 // the pointer itself and add this to m_outstanding_references.
334
335 // Increment the version of m_log_buffer, returning the previous version.
336 head_p h = increment_pointer_version(&m_log_buffer);
337
338 buffer = static_cast<LogBuffer *>(FREELIST_POINTER(h));
339 result_code = buffer->checkout_write(write_offset, bytes_needed);
340 bool decremented = false;
341
342 switch (result_code) {
343 case LogBuffer::LB_OK:
344 // checkout succeeded
345 retry = false;
346 break;
347
348 case LogBuffer::LB_FULL_ACTIVE_WRITERS:
349 case LogBuffer::LB_FULL_NO_WRITERS:
350 // no more room in current buffer, create a new one
351 new_buffer = new LogBuffer(Log::config, this, Log::config->log_buffer_size);
352
353 // swap the new buffer for the old one
354 INK_WRITE_MEMORY_BARRIER;
355
356 do {
357 INK_QUEUE_LD(old_h, m_log_buffer);
358 // we may depend on comparing the old pointer to the new pointer to detect buffer swaps
359 // without worrying about pointer collisions because we always allocate a new LogBuffer
360 // before freeing the old one
361 if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h)) {
362 ink_atomic_increment(&buffer->m_references, -1);
363
364 // another thread is already creating a new buffer,
365 // so delete new_buffer and try again next loop iteration
366 delete new_buffer;
367 new_buffer = nullptr;
368 break;
369 }
370 } while (write_pointer_version(&m_log_buffer, old_h, new_buffer, 0) == false);
371
372 if (FREELIST_POINTER(old_h) == FREELIST_POINTER(h)) {
373 ink_atomic_increment(&buffer->m_references, FREELIST_VERSION(old_h) - 1);
374
375 int idx = m_buffer_manager_idx++ % m_flush_threads;
376 Debug("log-logbuffer", "adding buffer %d to flush list after checkout", buffer->get_id());
377 m_buffer_manager[idx].add_to_flush_queue(buffer);
378 Log::preproc_notify[idx].signal();
379 buffer = nullptr;
380 }
381
382 decremented = true;
383 break;
384
385 case LogBuffer::LB_RETRY:
386 // no more room, but another thread should be taking care of creating a new buffer, so yield to let
387 // the other thread finish, then try again
388 std::this_thread::yield();
389 break;
390
391 case LogBuffer::LB_BUFFER_TOO_SMALL:
392 // return a null buffer to signal the caller that this transaction cannot be logged
393 retry = false;
394 break;
395
396 default:
397 ink_assert(false);
398 }
399
400 if (!decremented) {
401 head_p old_h;
402
403 // The do-while loop protects us from races while we're examining ptr(old_h) and ptr(h)
404 // (essentially an optimistic lock)
405 do {
406 INK_QUEUE_LD(old_h, m_log_buffer);
407 if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h)) {
408 // Another thread's allocated a new LogBuffer, we don't need to do anything more
409 break;
410 }
411
412 } while (!write_pointer_version(&m_log_buffer, old_h, FREELIST_POINTER(h), FREELIST_VERSION(old_h) - 1));
413
414 if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h)) {
415 // Another thread's allocated a new LogBuffer, meaning this LogObject is no longer referencing the old LogBuffer
416 ink_atomic_increment(&buffer->m_references, -1);
417 }
418 } else {
419 #ifdef __clang_analyzer__
420 if (new_buffer != nullptr) {
421 delete new_buffer;
422 }
423 #endif
424 }
425
426 } while (retry && write_offset); // if write_offset is null, we do
427 // not retry because we really do not want to write to the buffer,
428 // only to mark the buffer as full
429 if (result_code == LogBuffer::LB_BUFFER_TOO_SMALL) {
430 buffer = nullptr;
431 }
432
433 return buffer;
434 }
435
436 int
va_log(LogAccess * lad,const char * fmt,va_list ap)437 LogObject::va_log(LogAccess *lad, const char *fmt, va_list ap)
438 {
439 static const unsigned MAX_ENTRY = 16 * LOG_KILOBYTE; // 16K? Really?
440 char entry[MAX_ENTRY];
441 unsigned len = 0;
442
443 ink_assert(fmt != nullptr);
444 len = 0;
445
446 if (this->m_flags & LOG_OBJECT_FMT_TIMESTAMP) {
447 len = LogUtils::timestamp_to_str(LogUtils::timestamp(), entry, MAX_ENTRY);
448 if (unlikely(len <= 0 || len >= MAX_ENTRY)) {
449 return Log::FAIL;
450 }
451
452 // Add a space after the timestamp
453 entry[len++] = ' ';
454
455 if (unlikely(len >= MAX_ENTRY)) {
456 return Log::FAIL;
457 }
458 }
459
460 vsnprintf(&entry[len], MAX_ENTRY - len, fmt, ap);
461
462 // Now that we have an entry and it's length (len), we can place it
463 // into the associated logbuffer.
464 return this->log(lad, entry);
465 }
466
467 int
log(LogAccess * lad,const char * text_entry)468 LogObject::log(LogAccess *lad, const char *text_entry)
469 {
470 // Clang doesn't like initializing a view with nullptr, have to check.
471 return this->log(lad, std::string_view{text_entry ? text_entry : ""});
472 }
473
474 int
log(LogAccess * lad,std::string_view text_entry)475 LogObject::log(LogAccess *lad, std::string_view text_entry)
476 {
477 LogBuffer *buffer;
478 size_t offset = 0; // prevent warning
479 size_t bytes_needed = 0, bytes_used = 0;
480
481 // log to a pipe even if space is exhausted since pipe uses no space
482 // likewise, send data to a remote client even if local space is exhausted
483 // (if there is a remote client, m_logFile will be NULL
484 if (Log::config->logging_space_exhausted && !writes_to_pipe() && m_logFile) {
485 Debug("log", "logging space exhausted, can't write to:%s, drop this entry", m_logFile->get_name());
486 return Log::FULL;
487 }
488 // this verification must be done here in order to avoid 'dead' LogBuffers
489 // with none zero 'in usage' counters (see _checkout_write for more details)
490 if (!lad && text_entry.empty()) {
491 Note("Call to LogAccess without LAD or text entry; skipping");
492 return Log::FAIL;
493 }
494
495 if (lad && m_filter_list.toss_this_entry(lad)) {
496 Debug("log", "entry filtered, skipping ...");
497 return Log::SKIP;
498 }
499
500 if (lad && m_filter_list.wipe_this_entry(lad)) {
501 Debug("log", "entry wiped, ...");
502 }
503
504 if (lad && m_format->is_aggregate()) {
505 // marshal the field data into the temp space provided by the
506 // LogFormat object for aggregate formats
507 if (m_format->m_agg_marshal_space == nullptr) {
508 Note("No temp space to marshal aggregate fields into");
509 return Log::FAIL;
510 }
511
512 long time_now = LogUtils::timestamp();
513 m_format->m_field_list.marshal(lad, m_format->m_agg_marshal_space);
514
515 // step through each of the fields and update the LogField object
516 // with the newly-marshalled data
517 LogFieldList *fl = &m_format->m_field_list;
518 char *data_ptr = m_format->m_agg_marshal_space;
519 LogField *f;
520 int64_t val;
521 for (f = fl->first(); f; f = fl->next(f)) {
522 // convert to host order to do computations
523 val = (f->is_time_field()) ? time_now : *(reinterpret_cast<int64_t *>(data_ptr));
524 f->update_aggregate(val);
525 data_ptr += INK_MIN_ALIGN;
526 }
527
528 if (time_now < m_format->m_interval_next) {
529 Debug("log-agg",
530 "Time now = %ld, next agg = %ld; not time "
531 "for aggregate entry",
532 time_now, m_format->m_interval_next);
533 return Log::AGGR;
534 }
535 // can easily compute bytes_needed because all fields are INTs
536 // and will use INK_MIN_ALIGN each
537 bytes_needed = m_format->field_count() * INK_MIN_ALIGN;
538 } else if (lad) {
539 bytes_needed = m_format->m_field_list.marshal_len(lad);
540 } else if (!text_entry.empty()) {
541 bytes_needed = INK_ALIGN_DEFAULT(text_entry.size() + 1); // must include null terminator.
542 }
543
544 if (bytes_needed == 0) {
545 Debug("log-buffer", "Nothing to log, bytes_needed = 0");
546 return Log::SKIP;
547 }
548
549 // Now try to place this entry in the current LogBuffer.
550 buffer = _checkout_write(&offset, bytes_needed);
551
552 if (!buffer) {
553 Note("Skipping the current log entry for %s because its size (%zu) exceeds "
554 "the maximum payload space in a log buffer",
555 m_basename, bytes_needed);
556 return Log::FAIL;
557 }
558 //
559 // Ok, the checkout_write was successful, which means we have a valid
560 // offset into the current buffer. Marshal the entry into the buffer,
561 // and the commit (checkin) the changes.
562 //
563
564 if (lad && m_format->is_aggregate()) {
565 // the "real" entry data is contained in the LogField objects
566 // themselves, not in this lad.
567 bytes_used = m_format->m_field_list.marshal_agg(&(*buffer)[offset]);
568 ink_assert(bytes_needed >= bytes_used);
569 m_format->m_interval_next += m_format->m_interval_sec;
570 Debug("log-agg", "Aggregate entry created; next time is %ld", m_format->m_interval_next);
571 } else if (lad) {
572 bytes_used = m_format->m_field_list.marshal(lad, &(*buffer)[offset]);
573 ink_assert(bytes_needed >= bytes_used);
574 } else if (!text_entry.empty()) {
575 char *dst = &(*buffer)[offset];
576 memcpy(dst, text_entry.data(), text_entry.size());
577 memset(dst + text_entry.size(), 0, bytes_needed - text_entry.size());
578 }
579
580 buffer->checkin_write(offset);
581
582 return Log::LOG_OK;
583 }
584
585 void
_setup_rolling(LogConfig * cfg,Log::RollingEnabledValues rolling_enabled,int rolling_interval_sec,int rolling_offset_hr,int rolling_size_mb)586 LogObject::_setup_rolling(LogConfig *cfg, Log::RollingEnabledValues rolling_enabled, int rolling_interval_sec,
587 int rolling_offset_hr, int rolling_size_mb)
588 {
589 if (!LogRollingEnabledIsValid(static_cast<int>(rolling_enabled))) {
590 m_rolling_enabled = Log::NO_ROLLING;
591 m_rolling_interval_sec = 0;
592 m_rolling_offset_hr = 0;
593 m_rolling_size_mb = 0;
594 if (rolling_enabled != Log::NO_ROLLING) {
595 Warning("Valid rolling_enabled values are %d to %d, invalid value "
596 "(%d) specified for %s, rolling will be disabled for this file.",
597 Log::NO_ROLLING, Log::INVALID_ROLLING_VALUE - 1, rolling_enabled, m_filename);
598 } else {
599 Status("Rolling disabled for %s", m_filename);
600 }
601 } else {
602 // do checks for rolling based on time
603 //
604 if (rolling_enabled == Log::ROLL_ON_TIME_ONLY || rolling_enabled == Log::ROLL_ON_TIME_OR_SIZE ||
605 rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE) {
606 if (rolling_interval_sec < Log::MIN_ROLLING_INTERVAL_SEC) {
607 // check minimum
608 m_rolling_interval_sec = Log::MIN_ROLLING_INTERVAL_SEC;
609 } else if (rolling_interval_sec > Log::MAX_ROLLING_INTERVAL_SEC) {
610 // 1 day maximum
611 m_rolling_interval_sec = Log::MAX_ROLLING_INTERVAL_SEC;
612 } else if (Log::MAX_ROLLING_INTERVAL_SEC % rolling_interval_sec == 0) {
613 // OK, divides day evenly
614 m_rolling_interval_sec = rolling_interval_sec;
615 } else {
616 m_rolling_interval_sec = rolling_interval_sec;
617 // increase so it divides day evenly
618 while (Log::MAX_ROLLING_INTERVAL_SEC % ++m_rolling_interval_sec) {
619 ;
620 }
621 }
622
623 if (m_rolling_interval_sec != rolling_interval_sec) {
624 Note("Rolling interval adjusted from %d sec to %d sec for %s", rolling_interval_sec, m_rolling_interval_sec, m_filename);
625 }
626
627 if (rolling_offset_hr < 0 || rolling_offset_hr > 23) {
628 rolling_offset_hr = 0;
629 Note("Rolling offset out of bounds for %s, setting it to %d", m_filename, rolling_offset_hr);
630 }
631
632 m_rolling_offset_hr = rolling_offset_hr;
633 m_rolling_size_mb = 0; // it is safe to set it as 0, if we set SIZE rolling,
634 // it will be updated later
635 }
636
637 if (rolling_enabled == Log::ROLL_ON_SIZE_ONLY || rolling_enabled == Log::ROLL_ON_TIME_OR_SIZE ||
638 rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE) {
639 if (rolling_size_mb < 10) {
640 m_rolling_size_mb = 10;
641 Note("Rolling size invalid(%d) for %s, setting it to 10 MB", rolling_size_mb, m_filename);
642 } else {
643 m_rolling_size_mb = rolling_size_mb;
644 }
645 }
646 cfg->register_rolled_log_auto_delete(m_basename, m_min_rolled);
647 m_rolling_enabled = rolling_enabled;
648 }
649 }
650
651 unsigned
roll_files(long time_now)652 LogObject::roll_files(long time_now)
653 {
654 if (!m_rolling_enabled) {
655 return 0;
656 }
657
658 unsigned num_rolled = 0;
659 bool roll_on_time = false;
660 bool roll_on_size = false;
661
662 if (!time_now) {
663 time_now = LogUtils::timestamp();
664 }
665
666 if (m_rolling_enabled != Log::ROLL_ON_SIZE_ONLY) {
667 if (m_rolling_interval_sec > 0) {
668 // We make no assumptions about the current time not having
669 // changed underneath us. This could happen during daylight
670 // savings time adjustments, or if time is adjusted via NTP.
671 //
672 // For this reason we don't cache the number of seconds
673 // remaining until the next roll, but we calculate this figure
674 // every time ...
675 //
676 int secs_to_next = LogUtils::seconds_to_next_roll(time_now, m_rolling_offset_hr, m_rolling_interval_sec);
677
678 // ... likewise, we make sure we compute the absolute value
679 // of the seconds since the last roll (which would otherwise
680 // be negative if time "went back"). We will use this value
681 // to make sure we don't roll twice if time goes back shortly
682 // after rolling.
683 //
684 int secs_since_last = (m_last_roll_time < time_now ? time_now - m_last_roll_time : m_last_roll_time - time_now);
685
686 // number of seconds we allow for periodic_tasks() not to be
687 // called and still be able to roll
688 //
689 const int missed_window = 10;
690
691 roll_on_time =
692 ((secs_to_next == 0 || secs_to_next >= m_rolling_interval_sec - missed_window) && secs_since_last > missed_window);
693 }
694 }
695
696 if (m_rolling_enabled != Log::ROLL_ON_TIME_ONLY) {
697 if (m_rolling_size_mb) {
698 // Get file size and check if the file size if greater than the
699 // configured file size for rolling
700 roll_on_size = (get_file_size_bytes() > m_rolling_size_mb * LOG_MEGABYTE);
701 }
702 }
703
704 if ((roll_on_time && should_roll_on_time(m_rolling_enabled)) || (roll_on_size && should_roll_on_size(m_rolling_enabled)) ||
705 (roll_on_time && roll_on_size && m_rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE)) {
706 num_rolled = _roll_files(m_last_roll_time, time_now ? time_now : LogUtils::timestamp());
707 }
708
709 return num_rolled;
710 }
711
712 unsigned
_roll_files(long last_roll_time,long time_now)713 LogObject::_roll_files(long last_roll_time, long time_now)
714 {
715 unsigned num_rolled = 0;
716
717 if (m_logFile) {
718 // no need to roll if object writes to a pipe
719 if (!writes_to_pipe()) {
720 num_rolled += m_logFile->roll(last_roll_time, time_now, m_reopen_after_rolling);
721
722 if (Log::config->auto_delete_rolled_files && m_max_rolled > 0) {
723 m_logFile->trim_rolled(m_max_rolled);
724 }
725 }
726 }
727
728 m_last_roll_time = time_now;
729 return num_rolled;
730 }
731
732 void
check_buffer_expiration(long time_now)733 LogObject::check_buffer_expiration(long time_now)
734 {
735 LogBuffer *b = static_cast<LogBuffer *>(FREELIST_POINTER(m_log_buffer));
736 if (b && time_now > b->expiration_time()) {
737 force_new_buffer();
738 }
739 }
740
741 /*-------------------------------------------------------------------------
742 TextLogObject::TextLogObject
743 -------------------------------------------------------------------------*/
744 const LogFormat *TextLogObject::textfmt = MakeTextLogFormat();
745
TextLogObject(const char * name,const char * log_dir,bool timestamps,const char * header,Log::RollingEnabledValues rolling_enabled,int flush_threads,int rolling_interval_sec,int rolling_offset_hr,int rolling_size_mb,int rolling_max_count,int rolling_min_count,bool reopen_after_rolling)746 TextLogObject::TextLogObject(const char *name, const char *log_dir, bool timestamps, const char *header,
747 Log::RollingEnabledValues rolling_enabled, int flush_threads, int rolling_interval_sec,
748 int rolling_offset_hr, int rolling_size_mb, int rolling_max_count, int rolling_min_count,
749 bool reopen_after_rolling)
750 : LogObject(Log::config, TextLogObject::textfmt, log_dir, name, LOG_FILE_ASCII, header, rolling_enabled, flush_threads,
751 rolling_interval_sec, rolling_offset_hr, rolling_size_mb, /* auto_created */ false, rolling_max_count,
752 rolling_min_count, reopen_after_rolling)
753 {
754 if (timestamps) {
755 this->set_fmt_timestamps();
756 }
757 }
758
759 /*-------------------------------------------------------------------------
760 TextLogObject::write
761
762 This routine will take a printf-style format string and variable number
763 of arguments, and write them to the text file.
764
765 It really just creates a va_list and calls va_write to do the work.
766 Returns the number of bytes written to the file.
767 -------------------------------------------------------------------------*/
768 int
write(const char * format,...)769 TextLogObject::write(const char *format, ...)
770 {
771 int ret_val;
772
773 ink_assert(format != nullptr);
774 va_list ap;
775 va_start(ap, format);
776 ret_val = va_write(format, ap);
777 va_end(ap);
778
779 return ret_val;
780 }
781
782 /*-------------------------------------------------------------------------
783 TextLogObject::va_write
784
785 This routine will take a format string and va_list and write it as a
786 single entry (line) in the text file. If timestamps are on, then the
787 entry will be preceeded by a timestamp.
788
789 Returns ReturnCodeFlags.
790 -------------------------------------------------------------------------*/
791 int
va_write(const char * format,va_list ap)792 TextLogObject::va_write(const char *format, va_list ap)
793 {
794 return this->va_log(nullptr, format, ap);
795 }
796
797 /*-------------------------------------------------------------------------
798 LogObjectManager
799 -------------------------------------------------------------------------*/
800
LogObjectManager()801 LogObjectManager::LogObjectManager()
802 {
803 _APImutex = new ink_mutex;
804 ink_mutex_init(_APImutex);
805 }
806
~LogObjectManager()807 LogObjectManager::~LogObjectManager()
808 {
809 for (auto &_object : _objects) {
810 if (_object->refcount_dec() == 0) {
811 delete _object;
812 }
813 }
814
815 for (auto &_APIobject : _APIobjects) {
816 if (_APIobject->refcount_dec() == 0) {
817 delete _APIobject;
818 }
819 }
820
821 delete _APImutex;
822 }
823
824 int
_manage_object(LogObject * log_object,bool is_api_object,int maxConflicts)825 LogObjectManager::_manage_object(LogObject *log_object, bool is_api_object, int maxConflicts)
826 {
827 if (is_api_object) {
828 ACQUIRE_API_MUTEX("A LogObjectManager::_manage_object");
829 }
830
831 int retVal = _solve_internal_filename_conflicts(log_object, maxConflicts);
832
833 if (retVal == NO_FILENAME_CONFLICTS) {
834 if (retVal = _solve_filename_conflicts(log_object, maxConflicts), retVal == NO_FILENAME_CONFLICTS) {
835 // do filesystem checks
836 //
837 {
838 // no conflicts, add object to the list of managed objects
839 //
840 log_object->refcount_inc();
841 if (is_api_object) {
842 _APIobjects.push_back(log_object);
843 } else {
844 _objects.push_back(log_object);
845 }
846
847 ink_release_assert(retVal == NO_FILENAME_CONFLICTS);
848
849 Debug("log",
850 "LogObjectManager managing object %s (%s) "
851 "[signature = %" PRIu64 ", address = %p]",
852 log_object->get_base_filename(), log_object->get_full_filename(), log_object->get_signature(), log_object);
853
854 if (log_object->has_alternate_name()) {
855 Warning("The full path for the (%s) LogObject "
856 "with signature %" PRIu64 " "
857 "has been set to %s rather than %s because the latter "
858 "is being used by another LogObject",
859 log_object->get_base_filename(), log_object->get_signature(), log_object->get_full_filename(),
860 log_object->get_original_filename());
861 }
862 }
863 }
864 }
865
866 if (is_api_object) {
867 RELEASE_API_MUTEX("R LogObjectManager::_manage_object");
868 }
869
870 return retVal;
871 }
872
873 int
_solve_filename_conflicts(LogObject * log_object,int maxConflicts)874 LogObjectManager::_solve_filename_conflicts(LogObject *log_object, int maxConflicts)
875 {
876 int retVal = NO_FILENAME_CONFLICTS;
877
878 const char *filename = log_object->get_full_filename();
879
880 if (access(filename, F_OK)) {
881 if (errno != ENOENT) {
882 const char *msg = "Cannot access log file %s: %s";
883 const char *se = strerror(errno);
884
885 Error(msg, filename, se);
886 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename, se);
887 retVal = ERROR_ACCESSING_LOG_FILE;
888 }
889 } else {
890 // file exists, try to read metafile to get object signature
891 //
892 uint64_t signature = 0;
893 BaseMetaInfo meta_info(filename);
894 bool conflicts = true;
895
896 if (meta_info.file_open_successful()) {
897 bool got_sig = meta_info.get_log_object_signature(&signature);
898 uint64_t obj_sig = log_object->get_signature();
899
900 if (got_sig && signature == obj_sig) {
901 conflicts = false;
902 }
903 Debug("log",
904 "LogObjectManager::_solve_filename_conflicts\n"
905 "\tfilename = %s\n"
906 "\tmeta file signature = %" PRIu64 "\n"
907 "\tlog object signature = %" PRIu64 "\n"
908 "\tconflicts = %d",
909 filename, signature, obj_sig, conflicts);
910 }
911
912 if (conflicts) {
913 if (maxConflicts == 0) {
914 // do not take any action, and return an error status
915 //
916 const char *msg = "Cannot solve filename conflicts for log file %s";
917
918 Error(msg, filename);
919 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename);
920 retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
921 } else {
922 // Either the meta file could not be read, or the new object's
923 // signature and the metafile signature do not match.
924 // Roll the old filename so the new object can use the filename
925 // it requested (previously we used to rename the NEW file
926 // but now we roll the OLD file). However, if the log object writes to
927 // a pipe don't roll because rolling is not applicable to pipes.
928
929 bool roll_file = true;
930
931 if (log_object->writes_to_pipe()) {
932 // Verify whether the existing file is a pipe. If it is,
933 // disable the roll_file flag so we don't attempt rolling.
934 struct stat s;
935 if (stat(filename, &s) < 0) {
936 const char *msg = "Cannot stat log file %s: %s";
937 char *se = strerror(errno);
938
939 Error(msg, filename, se);
940 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename, se);
941 retVal = ERROR_DETERMINING_FILE_INFO;
942 roll_file = false;
943 } else {
944 if (S_ISFIFO(s.st_mode)) {
945 roll_file = false;
946 }
947 }
948 }
949 if (roll_file) {
950 Warning("File %s will be rolled because a LogObject with "
951 "different format is requesting the same "
952 "filename",
953 filename);
954 LogFile logfile(filename, nullptr, LOG_FILE_ASCII, 0);
955 if (logfile.open_file() == LogFile::LOG_FILE_NO_ERROR) {
956 long time_now = LogUtils::timestamp();
957
958 if (logfile.roll(time_now - log_object->get_rolling_interval(), time_now) == 0) {
959 // an error happened while trying to roll the file
960 //
961 _filename_resolution_abort(filename);
962 retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
963 }
964 } else {
965 _filename_resolution_abort(filename);
966 retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
967 }
968 }
969 }
970 }
971 }
972 return retVal;
973 }
974
975 void
_filename_resolution_abort(const char * filename)976 LogObjectManager::_filename_resolution_abort(const char *filename)
977 {
978 const char *msg = "Cannot roll log file %s to fix log "
979 "conflicts (filename or log format): %s";
980 const char *err = strerror(errno);
981 Error(msg, filename, err);
982 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename, err);
983 }
984
985 bool
_has_internal_filename_conflict(const char * filename,LogObjectList & objects)986 LogObjectManager::_has_internal_filename_conflict(const char *filename, LogObjectList &objects)
987 {
988 for (auto &object : objects) {
989 // an internal conflict exists if two objects request the
990 // same filename, regardless of the object signatures, since
991 // two objects writing to the same file would produce a
992 // log with duplicate entries and non monotonic timestamps
993 if (strcmp(object->get_full_filename(), filename) == 0) {
994 return true;
995 }
996 }
997 return false;
998 }
999
1000 int
_solve_internal_filename_conflicts(LogObject * log_object,int maxConflicts,int fileNum)1001 LogObjectManager::_solve_internal_filename_conflicts(LogObject *log_object, int maxConflicts, int fileNum)
1002 {
1003 int retVal = NO_FILENAME_CONFLICTS;
1004 const char *filename = log_object->get_full_filename();
1005
1006 if (_has_internal_filename_conflict(filename, _objects) || _has_internal_filename_conflict(filename, _APIobjects)) {
1007 if (fileNum < maxConflicts) {
1008 char new_name[MAXPATHLEN];
1009
1010 snprintf(new_name, sizeof(new_name), "%s%s%d", log_object->get_original_filename(), LOGFILE_SEPARATOR_STRING, ++fileNum);
1011 log_object->rename(new_name);
1012 retVal = _solve_internal_filename_conflicts(log_object, maxConflicts, fileNum);
1013 } else {
1014 const char *msg = "Cannot solve filename conflicts for log file %s";
1015
1016 Error(msg, filename);
1017 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename);
1018 retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
1019 }
1020 }
1021 return retVal;
1022 }
1023
1024 LogObject *
get_object_with_signature(uint64_t signature)1025 LogObjectManager::get_object_with_signature(uint64_t signature)
1026 {
1027 for (auto obj : this->_objects) {
1028 if (obj->get_signature() == signature) {
1029 return obj;
1030 }
1031 }
1032 return nullptr;
1033 }
1034
1035 void
check_buffer_expiration(long time_now)1036 LogObjectManager::check_buffer_expiration(long time_now)
1037 {
1038 for (auto &_object : this->_objects) {
1039 _object->check_buffer_expiration(time_now);
1040 }
1041
1042 ACQUIRE_API_MUTEX("A LogObjectManager::check_buffer_expiration");
1043
1044 for (auto &_APIobject : this->_APIobjects) {
1045 _APIobject->check_buffer_expiration(time_now);
1046 }
1047
1048 RELEASE_API_MUTEX("R LogObjectManager::check_buffer_expiration");
1049 }
1050
1051 size_t
preproc_buffers(int idx)1052 LogObjectManager::preproc_buffers(int idx)
1053 {
1054 size_t buffers_preproced = 0;
1055
1056 for (auto &_object : this->_objects) {
1057 buffers_preproced += _object->preproc_buffers(idx);
1058 }
1059
1060 ACQUIRE_API_MUTEX("A LogObjectManager::preproc_buffers");
1061
1062 for (auto &_APIobject : this->_APIobjects) {
1063 buffers_preproced += _APIobject->preproc_buffers(idx);
1064 }
1065
1066 RELEASE_API_MUTEX("R LogObjectManager::preproc_buffers");
1067
1068 return buffers_preproced;
1069 }
1070
1071 bool
unmanage_api_object(LogObject * logObject)1072 LogObjectManager::unmanage_api_object(LogObject *logObject)
1073 {
1074 if (!logObject) {
1075 return false;
1076 }
1077
1078 ACQUIRE_API_MUTEX("A LogObjectManager::unmanage_api_object");
1079
1080 auto index = std::find(this->_APIobjects.begin(), this->_APIobjects.end(), logObject);
1081
1082 if (index != this->_APIobjects.end()) {
1083 this->_APIobjects.erase(index);
1084
1085 // Force a buffer flush, then schedule this LogObject to be deleted on the eventProcessor.
1086 logObject->force_new_buffer();
1087 new_Derefer(logObject, HRTIME_SECONDS(60));
1088
1089 RELEASE_API_MUTEX("R LogObjectManager::unmanage_api_object");
1090 return true;
1091 }
1092
1093 RELEASE_API_MUTEX("R LogObjectManager::unmanage_api_object");
1094 return false;
1095 }
1096
1097 void
add_filter_to_all(LogFilter * filter)1098 LogObjectManager::add_filter_to_all(LogFilter *filter)
1099 {
1100 for (unsigned i = 0; i < this->_objects.size(); i++) {
1101 _objects[i]->add_filter(filter);
1102 }
1103 }
1104
1105 void
open_local_pipes()1106 LogObjectManager::open_local_pipes()
1107 {
1108 // for all local objects that write to a pipe, call open_file to force
1109 // the creation of the pipe so that any potential reader can see it
1110 //
1111 for (unsigned i = 0; i < this->_objects.size(); i++) {
1112 LogObject *obj = _objects[i];
1113 if (obj->writes_to_pipe()) {
1114 obj->m_logFile->open_file();
1115 }
1116 }
1117 }
1118
1119 void
transfer_objects(LogObjectManager & old_mgr)1120 LogObjectManager::transfer_objects(LogObjectManager &old_mgr)
1121 {
1122 unsigned num_kept_objects = 0;
1123
1124 Debug("log-config-transfer", "transferring objects from LogObjectManager %p, to %p", &old_mgr, this);
1125
1126 if (is_debug_tag_set("log-config-transfer")) {
1127 Debug("log-config-transfer", "TRANSFER OBJECTS: list of old objects");
1128 for (auto &_object : old_mgr._objects) {
1129 Debug("log-config-transfer", "%s", _object->get_original_filename());
1130 }
1131
1132 Debug("log-config-transfer", "TRANSFER OBJECTS : list of new objects");
1133 for (unsigned i = 0; i < this->_objects.size(); i++) {
1134 Debug("log-config-transfer", "%s", _objects[i]->get_original_filename());
1135 }
1136 }
1137
1138 // Transfer the API objects from the old manager. The old manager will retain its refcount.
1139 for (auto &_APIobject : old_mgr._APIobjects) {
1140 manage_api_object(_APIobject);
1141 }
1142
1143 for (auto old_obj : old_mgr._objects) {
1144 LogObject *new_obj;
1145
1146 Debug("log-config-transfer", "examining existing object %s", old_obj->get_base_filename());
1147
1148 // See if any of the new objects is just a copy of an old one. If so, transfer the
1149 // old one to the new manager and delete the new one. We don't use Vec::in here because
1150 // we need to compare the object hash, not the pointers.
1151 for (unsigned j = 0; j < _objects.size(); j++) {
1152 new_obj = _objects[j];
1153
1154 Debug("log-config-transfer", "comparing existing object %s to new object %s", old_obj->get_base_filename(),
1155 new_obj->get_base_filename());
1156
1157 if (*new_obj == *old_obj) {
1158 Debug("log-config-transfer", "keeping existing object %s", old_obj->get_base_filename());
1159
1160 old_obj->refcount_inc();
1161 this->_objects[j] = old_obj;
1162
1163 if (new_obj->refcount_dec() == 0) {
1164 delete new_obj;
1165 }
1166 ++num_kept_objects;
1167 break;
1168 }
1169 }
1170 }
1171
1172 if (is_debug_tag_set("log-config-transfer")) {
1173 Debug("log-config-transfer", "Log Object List after transfer:");
1174 display();
1175 }
1176 }
1177
1178 unsigned
roll_files(long time_now)1179 LogObjectManager::roll_files(long time_now)
1180 {
1181 int num_rolled = 0;
1182
1183 for (auto &_object : this->_objects) {
1184 num_rolled += _object->roll_files(time_now);
1185 }
1186
1187 ACQUIRE_API_MUTEX("A LogObjectManager::roll_files");
1188
1189 for (auto &_APIobject : this->_APIobjects) {
1190 num_rolled += _APIobject->roll_files(time_now);
1191 }
1192
1193 RELEASE_API_MUTEX("R LogObjectManager::roll_files");
1194
1195 return num_rolled;
1196 }
1197
1198 void
reopen_moved_log_files()1199 LogObjectManager::reopen_moved_log_files()
1200 {
1201 for (auto &_object : this->_objects) {
1202 _object->m_logFile->reopen_if_moved();
1203 }
1204
1205 ACQUIRE_API_MUTEX("A LogObjectManager::reopen_moved_log_files");
1206
1207 for (auto &_APIobject : this->_APIobjects) {
1208 _APIobject->m_logFile->reopen_if_moved();
1209 }
1210
1211 RELEASE_API_MUTEX("R LogObjectManager::reopen_moved_log_files");
1212 }
1213
1214 void
display(FILE * str)1215 LogObjectManager::display(FILE *str)
1216 {
1217 for (unsigned i = 0; i < this->_objects.size(); i++) {
1218 _objects[i]->display(str);
1219 }
1220
1221 ACQUIRE_API_MUTEX("A LogObjectManager::display");
1222 for (unsigned i = 0; i < this->_APIobjects.size(); i++) {
1223 _APIobjects[i]->display(str);
1224 }
1225 RELEASE_API_MUTEX("R LogObjectManager::display");
1226 }
1227
1228 LogObject *
find_by_format_name(const char * name) const1229 LogObjectManager::find_by_format_name(const char *name) const
1230 {
1231 for (auto _object : this->_objects) {
1232 if (_object && _object->m_format->name_id() == LogFormat::id_from_name(name)) {
1233 return _object;
1234 }
1235 }
1236 return nullptr;
1237 }
1238
1239 int
log(LogAccess * lad)1240 LogObjectManager::log(LogAccess *lad)
1241 {
1242 int ret = Log::SKIP;
1243 ProxyMutex *mutex = this_thread()->mutex.get();
1244
1245 for (unsigned i = 0; i < this->_objects.size(); i++) {
1246 ret |= _objects[i]->log(lad);
1247 }
1248
1249 //
1250 // The bit-field code in *ret* are priority chain:
1251 // FAIL > FULL > LOG_OK > AGGR > SKIP
1252 // The if-statement should keep step with the priority order.
1253 //
1254 if (unlikely(ret & Log::FAIL)) {
1255 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_fail_stat, 1);
1256 } else if (unlikely(ret & Log::FULL)) {
1257 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_full_stat, 1);
1258 } else if (likely(ret & Log::LOG_OK)) {
1259 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_ok_stat, 1);
1260 } else if (unlikely(ret & Log::AGGR)) {
1261 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_aggr_stat, 1);
1262 } else if (likely(ret & Log::SKIP)) {
1263 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_skip_stat, 1);
1264 } else {
1265 ink_release_assert(!"Unexpected result");
1266 }
1267
1268 return ret;
1269 }
1270
1271 void
flush_all_objects()1272 LogObjectManager::flush_all_objects()
1273 {
1274 for (auto &_object : this->_objects) {
1275 _object->force_new_buffer();
1276 }
1277
1278 ACQUIRE_API_MUTEX("A LogObjectManager::flush_all_objects");
1279
1280 for (auto &_APIobject : this->_APIobjects) {
1281 _APIobject->force_new_buffer();
1282 }
1283
1284 RELEASE_API_MUTEX("R LogObjectManager::flush_all_objects");
1285 }
1286
1287 #if TS_HAS_TESTS
1288
1289 static LogObject *
MakeTestLogObject(const char * name)1290 MakeTestLogObject(const char *name)
1291 {
1292 const char *tmpdir = getenv("TMPDIR");
1293 LogFormat format("testfmt", nullptr);
1294
1295 if (!tmpdir) {
1296 tmpdir = "/tmp";
1297 }
1298
1299 return new LogObject(Log::config, &format, tmpdir, name, LOG_FILE_ASCII /* file_format */, name /* header */,
1300 Log::ROLL_ON_TIME_ONLY /* rolling_enabled */, 1 /* flush_threads */);
1301 }
1302
REGRESSION_TEST(LogObjectManager_Transfer)1303 REGRESSION_TEST(LogObjectManager_Transfer)(RegressionTest *t, int /* atype ATS_UNUSED */, int *pstatus)
1304 {
1305 TestBox box(t, pstatus);
1306
1307 // There used to be a lot of confusion around whether LogObjects were owned by ome or more LogObjectManager
1308 // objects, or handed off to static storage in the Log class. This test just verifies that this is no longer
1309 // the case.
1310 {
1311 LogObjectManager mgr1;
1312 LogObjectManager mgr2;
1313
1314 mgr1.manage_object(MakeTestLogObject("object1"));
1315 mgr1.manage_object(MakeTestLogObject("object2"));
1316 mgr1.manage_object(MakeTestLogObject("object3"));
1317 mgr1.manage_object(MakeTestLogObject("object4"));
1318
1319 mgr2.transfer_objects(mgr1);
1320
1321 rprintf(t, "mgr1 has %d objects, mgr2 has %d objects\n", static_cast<int>(mgr1.get_num_objects()),
1322 static_cast<int>(mgr2.get_num_objects()));
1323 box.check(mgr1.get_num_objects() == 0, "Testing that manager 1 has 0 objects");
1324 box.check(mgr2.get_num_objects() == 4, "Testing that manager 2 has 4 objects");
1325
1326 rprintf(t, "running Log::periodoc_tasks()\n");
1327 Log::periodic_tasks(Thread::get_hrtime() / HRTIME_SECOND);
1328 rprintf(t, "Log::periodoc_tasks() done\n");
1329 }
1330
1331 box = REGRESSION_TEST_PASSED;
1332 }
1333
1334 #endif
1335