00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 #include "libts.h"
00030 
00031 
00032 #include "Error.h"
00033 #include "P_EventSystem.h"
00034 #include "LogUtils.h"
00035 #include "LogField.h"
00036 #include "LogObject.h"
00037 #include "LogConfig.h"
00038 #include "LogAccess.h"
00039 #include "Log.h"
00040 #include "ts/TestBox.h"
00041 
00042 static bool
00043 should_roll_on_time(Log::RollingEnabledValues roll) {
00044   return roll == Log::ROLL_ON_TIME_ONLY || roll == Log::ROLL_ON_TIME_OR_SIZE;
00045 }
00046 
00047 static bool
00048 should_roll_on_size(Log::RollingEnabledValues roll) {
00049   return roll == Log::ROLL_ON_SIZE_ONLY || roll == Log::ROLL_ON_TIME_OR_SIZE;
00050 }
00051 
00052 size_t
00053 LogBufferManager::preproc_buffers(LogBufferSink *sink) {
00054   SList(LogBuffer, write_link) q(write_list.popall()), new_q;
00055   LogBuffer *b = NULL;
00056   while ((b = q.pop())) {
00057     if (b->m_references || b->m_state.s.num_writers) {
00058       
00059       write_list.push(b);
00060     } else if (_num_flush_buffers > FLUSH_ARRAY_SIZE) {
00061       ink_atomic_increment(&_num_flush_buffers, -1);
00062       Warning("Dropping log buffer, can't keep up.");
00063       RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding,
00064                      log_stat_bytes_lost_before_preproc_stat,
00065                      b->header()->byte_count);
00066       delete b;
00067     } else {
00068       new_q.push(b);
00069     }
00070   }
00071 
00072   int prepared = 0;
00073   while ((b = new_q.pop())) {
00074     b->update_header_data();
00075     sink->preproc_and_try_delete(b);
00076     ink_atomic_increment(&_num_flush_buffers, -1);
00077     prepared++;
00078   }
00079 
00080   Debug("log-logbuffer", "prepared %d buffers", prepared);
00081   return prepared;
00082 }
00083 
00084 
00085 
00086 
00087 
00088 LogObject::LogObject(const LogFormat *format, const char *log_dir,
00089                      const char *basename, LogFileFormat file_format,
00090                      const char *header, Log::RollingEnabledValues rolling_enabled,
00091                      int flush_threads, int rolling_interval_sec,
00092                      int rolling_offset_hr, int rolling_size_mb,
00093                      bool auto_created):
00094       m_auto_created(auto_created),
00095       m_alt_filename (NULL),
00096       m_flags (0),
00097       m_signature (0),
00098       m_flush_threads (flush_threads),
00099       m_rolling_interval_sec (rolling_interval_sec),
00100       m_rolling_offset_hr (rolling_offset_hr),
00101       m_rolling_size_mb (rolling_size_mb),
00102       m_last_roll_time(0),
00103       m_buffer_manager_idx(0)
00104 {
00105     ink_assert (format != NULL);
00106     m_format = new LogFormat(*format);
00107     m_buffer_manager = new LogBufferManager[m_flush_threads];
00108 
00109     if (file_format == LOG_FILE_BINARY) {
00110         m_flags |= BINARY;
00111     } else if (file_format == LOG_FILE_PIPE) {
00112         m_flags |= WRITES_TO_PIPE;
00113     }
00114 
00115     generate_filenames(log_dir, basename, file_format);
00116 
00117     
00118     m_signature = compute_signature(m_format, m_basename, m_flags);
00119 
00120     
00121     
00122     
00123     m_logFile = new LogFile(m_filename, header, file_format,
00124                             m_signature,
00125                             Log::config->ascii_buffer_size,
00126                             Log::config->max_line_size);
00127 
00128     LogBuffer *b = new LogBuffer (this, Log::config->log_buffer_size);
00129     ink_assert(b);
00130     SET_FREELIST_POINTER_VERSION(m_log_buffer, b, 0);
00131 
00132     _setup_rolling(rolling_enabled, rolling_interval_sec, rolling_offset_hr, rolling_size_mb);
00133 
00134     Debug("log-config", "exiting LogObject constructor, filename=%s this=%p",
00135           m_filename, this);
00136 }
00137 
00138 LogObject::LogObject(LogObject& rhs)
00139   : m_basename(ats_strdup(rhs.m_basename)),
00140     m_filename(ats_strdup(rhs.m_filename)),
00141     m_alt_filename(ats_strdup(rhs.m_alt_filename)),
00142     m_flags(rhs.m_flags),
00143     m_signature(rhs.m_signature),
00144     m_flush_threads(rhs.m_flush_threads),
00145     m_rolling_interval_sec(rhs.m_rolling_interval_sec),
00146     m_last_roll_time(rhs.m_last_roll_time)
00147 {
00148     m_format = new LogFormat(*(rhs.m_format));
00149     m_buffer_manager = new LogBufferManager[m_flush_threads];
00150 
00151     if (rhs.m_logFile) {
00152         m_logFile = new LogFile(*(rhs.m_logFile));
00153     } else {
00154         m_logFile = NULL;
00155     }
00156 
00157     LogFilter *filter;
00158     for (filter = rhs.m_filter_list.first(); filter;
00159             filter = rhs.m_filter_list.next (filter)) {
00160         add_filter (filter);
00161     }
00162 
00163     LogHost *host;
00164     for (host = rhs.m_host_list.first(); host;
00165             host = rhs.m_host_list.next (host)) {
00166         add_loghost (host);
00167     }
00168 
00169     
00170     
00171     LogBuffer *b = new LogBuffer (this, Log::config->log_buffer_size);
00172     ink_assert(b);
00173     SET_FREELIST_POINTER_VERSION(m_log_buffer, b, 0);
00174 
00175     Debug("log-config", "exiting LogObject copy constructor, "
00176           "filename=%s this=%p", m_filename, this);
00177 }
00178 
00179 LogObject::~LogObject()
00180 {
00181   Debug("log-config", "entering LogObject destructor, this=%p", this);
00182 
00183   preproc_buffers();
00184 
00185   
00186   if (is_collation_client()) {
00187     if (m_host_list.count()) {
00188       m_host_list.clear();
00189     }
00190   }
00191   ats_free(m_basename);
00192   ats_free(m_filename);
00193   ats_free(m_alt_filename);
00194   delete m_format;
00195   delete[] m_buffer_manager;
00196   delete (LogBuffer*)FREELIST_POINTER(m_log_buffer);
00197 }
00198 
00199 
00200 
00201 
00202 
00203 
00204 
00205 
00206 
00207 
00208 
00209 
00210 
00211 void
00212 LogObject::generate_filenames(const char *log_dir, const char *basename, LogFileFormat file_format)
00213 {
00214   ink_assert(log_dir && basename);
00215 
00216   int i = -1, len = 0;
00217   char c;
00218   while (c = basename[len], c != 0) {
00219     if (c == '.') {
00220       i = len;
00221     }
00222     ++len;
00223   }
00224   if (i == len - 1) {
00225     --len;
00226   };                            
00227 
00228   const char *ext = 0;
00229   int ext_len = 0;
00230   if (i < 0) {                  
00231     switch (file_format) {
00232     case LOG_FILE_ASCII:
00233       ext = LOG_FILE_ASCII_OBJECT_FILENAME_EXTENSION;
00234       ext_len = 4;
00235       break;
00236     case LOG_FILE_BINARY:
00237       ext = LOG_FILE_BINARY_OBJECT_FILENAME_EXTENSION;
00238       ext_len = 5;
00239       break;
00240     case LOG_FILE_PIPE:
00241       ext = LOG_FILE_PIPE_OBJECT_FILENAME_EXTENSION;
00242       ext_len = 5;
00243       break;
00244     default:
00245       ink_assert(!"unknown file format");
00246     }
00247   }
00248 
00249   int dir_len = (int) strlen(log_dir);
00250   int basename_len = len + ext_len + 1; 
00251   int total_len = dir_len + 1 + basename_len;   
00252 
00253   m_filename = (char *)ats_malloc(total_len);
00254   m_basename = (char *)ats_malloc(basename_len);
00255 
00256   memcpy(m_filename, log_dir, dir_len);
00257   m_filename[dir_len++] = '/';
00258   memcpy(&m_filename[dir_len], basename, len);
00259   memcpy(m_basename, basename, len);
00260 
00261   if (ext_len) {
00262     memcpy(&m_filename[dir_len + len], ext, ext_len);
00263     memcpy(&m_basename[len], ext, ext_len);
00264   }
00265   m_filename[total_len - 1] = 0;
00266   m_basename[basename_len - 1] = 0;
00267 }
00268 
00269 
00270 
00271 void
00272 LogObject::rename(char *new_name)
00273 {
00274   
00275   
00276   
00277   
00278   ats_free(m_alt_filename);
00279   m_alt_filename = ats_strdup(new_name);
00280   m_logFile->change_name(new_name);
00281 }
00282 
00283 
00284 void
00285 LogObject::add_filter(LogFilter * filter, bool copy)
00286 {
00287   if (!filter) {
00288     return;
00289   }
00290   m_filter_list.add(filter, copy);
00291 }
00292 
00293 
00294 void
00295 LogObject::set_filter_list(const LogFilterList & list, bool copy)
00296 {
00297   LogFilter *f;
00298 
00299   m_filter_list.clear();
00300   for (f = list.first(); f != NULL; f = list.next(f)) {
00301     m_filter_list.add(f, copy);
00302   }
00303   m_filter_list.set_conjunction(list.does_conjunction());
00304 }
00305 
00306 
00307 void
00308 LogObject::add_loghost(LogHost * host, bool copy)
00309 {
00310   if (!host) {
00311     return;
00312   }
00313   m_host_list.add(host, copy);
00314 
00315   
00316   
00317   
00318   
00319   m_logFile.clear();
00320 }
00321 
00322 
00323 
00324 
00325 
00326 
00327 uint64_t LogObject::compute_signature(LogFormat * format, char *filename, unsigned int flags)
00328 {
00329   char *fl = format->fieldlist();
00330   char *ps = format->printf_str();
00331   uint64_t signature = 0;
00332 
00333   if (fl && ps && filename) {
00334     int buf_size = strlen(fl) + strlen(ps) + strlen(filename) + 2;
00335     char *buffer = (char *)ats_malloc(buf_size);
00336 
00337     ink_string_concatenate_strings(buffer, fl, ps, filename, flags & LogObject::BINARY ? "B" :
00338                                    (flags & LogObject::WRITES_TO_PIPE ? "P" : "A"), NULL);
00339 
00340     CryptoHash hash;
00341     MD5Context().hash_immediate(hash, buffer, buf_size - 1);
00342     signature = hash.fold();
00343 
00344     ats_free(buffer);
00345   }
00346   return signature;
00347 }
00348 
00349 
00350 void
00351 LogObject::display(FILE * fd)
00352 {
00353   fprintf(fd, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
00354   fprintf(fd, "LogObject [%p]: format = %s (%p)\nbasename = %s\n" "flags = %u\n"
00355           "signature = %" PRIu64 "\n",
00356           this, m_format->name(), m_format, m_basename, m_flags, m_signature);
00357   if (is_collation_client()) {
00358     m_host_list.display(fd);
00359   } else {
00360     fprintf(fd, "full path = %s\n", get_full_filename());
00361   }
00362   m_filter_list.display(fd);
00363   fprintf(fd, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
00364 }
00365 
00366 
00367 void
00368 LogObject::displayAsXML(FILE * fd, bool extended)
00369 {
00370   if (extended) {
00371     
00372     m_format->displayAsXML(fd);
00373     m_filter_list.display_as_XML(fd);
00374   }
00375 
00376   fprintf(fd,
00377           "<LogObject>\n"
00378           "  <Mode        = \"%s\"/>\n"
00379           "  <Format      = \"%s\"/>\n"
00380           "  <Filename    = \"%s\"/>\n", (m_flags & BINARY ? "binary" : "ascii"), m_format->name(), m_filename);
00381 
00382   LogFilter *filter;
00383   for (filter = m_filter_list.first(); filter != NULL; filter = m_filter_list.next(filter)) {
00384     fprintf(fd, "  <Filter      = \"%s\"/>\n", filter->name());
00385   }
00386 
00387   LogHost *host;
00388   for (host = m_host_list.first(); host != NULL; host = m_host_list.next(host)) {
00389     fprintf(fd, "  <LogHostName = \"%s\"/>\n", host->name());
00390   }
00391 
00392   fprintf(fd, "</LogObject>\n");
00393 }
00394 
00395 
00396 LogBuffer *
00397 LogObject::_checkout_write(size_t * write_offset, size_t bytes_needed) {
00398   LogBuffer::LB_ResultCode result_code;
00399   LogBuffer *buffer;
00400   LogBuffer *new_buffer;
00401   bool retry = true;
00402 
00403   do {
00404     
00405     
00406     head_p h;
00407     int result = 0;
00408     do {
00409       INK_QUEUE_LD(h, m_log_buffer);
00410       head_p new_h;
00411       SET_FREELIST_POINTER_VERSION(new_h, FREELIST_POINTER(h), FREELIST_VERSION(h) + 1);
00412 #if TS_HAS_128BIT_CAS
00413        result = ink_atomic_cas((__int128_t*) &m_log_buffer.data, h.data, new_h.data);
00414 #else
00415        result = ink_atomic_cas((int64_t *) &m_log_buffer.data, h.data, new_h.data);
00416 #endif
00417     } while (!result);
00418     buffer = (LogBuffer*)FREELIST_POINTER(h);
00419     result_code = buffer->checkout_write(write_offset, bytes_needed);
00420     bool decremented = false;
00421 
00422     switch (result_code) {
00423     case LogBuffer::LB_OK:
00424       
00425       
00426       retry = false;
00427       break;
00428 
00429     case LogBuffer::LB_FULL_ACTIVE_WRITERS:
00430     case LogBuffer::LB_FULL_NO_WRITERS:
00431       
00432       new_buffer = new LogBuffer(this, Log::config->log_buffer_size);
00433 
00434       
00435       INK_WRITE_MEMORY_BARRIER;
00436       head_p old_h;
00437       do {
00438         INK_QUEUE_LD(old_h, m_log_buffer);
00439         if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h)) {
00440           ink_atomic_increment(&buffer->m_references, -1);
00441 
00442           
00443           
00444           delete new_buffer;
00445           break;
00446         }
00447         head_p tmp_h;
00448         SET_FREELIST_POINTER_VERSION(tmp_h, new_buffer, 0);
00449 #if TS_HAS_128BIT_CAS
00450        result = ink_atomic_cas((__int128_t*) &m_log_buffer.data, old_h.data, tmp_h.data);
00451 #else
00452        result = ink_atomic_cas((int64_t *) &m_log_buffer.data, old_h.data, tmp_h.data);
00453 #endif
00454       } while (!result);
00455       if (FREELIST_POINTER(old_h) == FREELIST_POINTER(h)) {
00456         ink_atomic_increment(&buffer->m_references, FREELIST_VERSION(old_h) - 1);
00457 
00458         int idx = m_buffer_manager_idx++ % m_flush_threads;
00459         Debug("log-logbuffer", "adding buffer %d to flush list after checkout", buffer->get_id());
00460         m_buffer_manager[idx].add_to_flush_queue(buffer);
00461         Log::preproc_notify[idx].signal();
00462 
00463       }
00464       decremented = true;
00465       break;
00466 
00467     case LogBuffer::LB_RETRY:
00468       
00469       
00470       
00471       break;
00472 
00473     case LogBuffer::LB_BUFFER_TOO_SMALL:
00474 
00475       
00476       
00477       
00478       retry = false;
00479       buffer = NULL;
00480       break;
00481 
00482     default:
00483       ink_assert(false);
00484     }
00485     if (!decremented) {
00486       head_p old_h;
00487       do {
00488         INK_QUEUE_LD(old_h, m_log_buffer);
00489         if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h))
00490           break;
00491         head_p tmp_h;
00492         SET_FREELIST_POINTER_VERSION(tmp_h, FREELIST_POINTER(h), FREELIST_VERSION(old_h) - 1);
00493 #if TS_HAS_128BIT_CAS
00494        result = ink_atomic_cas((__int128_t*) &m_log_buffer.data, old_h.data, tmp_h.data);
00495 #else
00496        result = ink_atomic_cas((int64_t *) &m_log_buffer.data, old_h.data, tmp_h.data);
00497 #endif
00498       } while (!result);
00499       if (FREELIST_POINTER(old_h) != FREELIST_POINTER(h))
00500         ink_atomic_increment(&buffer->m_references, -1);
00501     }
00502   } while (retry && write_offset);      
00503   
00504   
00505   
00506 
00507   return buffer;
00508 }
00509 
00510 
00511 int
00512 LogObject::va_log(LogAccess * lad, const char * fmt, va_list ap)
00513 {
00514   static const unsigned MAX_ENTRY = 16 * LOG_KILOBYTE; 
00515   char entry[MAX_ENTRY];
00516   unsigned len = 0;
00517 
00518   ink_assert(fmt != NULL);
00519   len = 0;
00520 
00521   if (this->m_flags & LOG_OBJECT_FMT_TIMESTAMP) {
00522     len = LogUtils::timestamp_to_str(LogUtils::timestamp(), entry, MAX_ENTRY);
00523     if (len <= 0) {
00524       return Log::FAIL;
00525     }
00526 
00527     
00528     entry[len++] = ' ';
00529 
00530     if (len >= MAX_ENTRY) {
00531       return Log::FAIL;
00532     }
00533   }
00534 
00535   vsnprintf(&entry[len], MAX_ENTRY - len, fmt, ap);
00536 
00537   
00538   
00539   return this->log(lad, entry);
00540 }
00541 
00542 int
00543 LogObject::log(LogAccess * lad, const char *text_entry)
00544 {
00545   LogBuffer *buffer;
00546   size_t offset = 0;            
00547   size_t bytes_needed = 0, bytes_used = 0;
00548 
00549   
00550   
00551   
00552   if (Log::config->logging_space_exhausted && !writes_to_pipe() && m_logFile) {
00553     Debug("log", "logging space exhausted, can't write to:%s, drop this entry", m_logFile->get_name());
00554     return Log::FULL;
00555   }
00556   
00557   
00558   if (!lad && !text_entry) {
00559     Note("Call to LogAccess without LAD or text entry; skipping");
00560     return Log::FAIL;
00561   }
00562 
00563   if (lad && m_filter_list.toss_this_entry(lad)) {
00564     Debug("log", "entry filtered, skipping ...");
00565     return Log::SKIP;
00566   }
00567 
00568   if (lad && m_filter_list.wipe_this_entry(lad)) {
00569     Debug("log", "entry wiped, ...");
00570   }
00571 
00572   if (lad && m_format->is_aggregate()) {
00573     
00574     
00575     if (m_format->m_agg_marshal_space == NULL) {
00576       Note("No temp space to marshal aggregate fields into");
00577       return Log::FAIL;
00578     }
00579 
00580     long time_now = LogUtils::timestamp();
00581     m_format->m_field_list.marshal(lad, m_format->m_agg_marshal_space);
00582 
00583     
00584     
00585     LogFieldList *fl = &m_format->m_field_list;
00586     char *data_ptr = m_format->m_agg_marshal_space;
00587     LogField *f;
00588     int64_t val;
00589     for (f = fl->first(); f; f = fl->next(f)) {
00590 
00591       
00592       val = (f->is_time_field())? time_now : *((int64_t *) data_ptr);
00593       f->update_aggregate(val);
00594       data_ptr += INK_MIN_ALIGN;
00595     }
00596 
00597     if (time_now < m_format->m_interval_next) {
00598       Debug("log-agg", "Time now = %ld, next agg = %ld; not time "
00599             "for aggregate entry", time_now, m_format->m_interval_next);
00600       return Log::AGGR;
00601     }
00602     
00603     
00604     bytes_needed = m_format->field_count() * INK_MIN_ALIGN;
00605   } else if (lad) {
00606     bytes_needed = m_format->m_field_list.marshal_len(lad);
00607   } else if (text_entry) {
00608     bytes_needed = LogAccess::strlen(text_entry);
00609   }
00610 
00611   if (bytes_needed == 0) {
00612     Debug("log-buffer", "Nothing to log, bytes_needed = 0");
00613     return Log::SKIP;
00614   }
00615 
00616   
00617   buffer = _checkout_write(&offset, bytes_needed);
00618 
00619   if (!buffer) {
00620     Note("Skipping the current log entry for %s because its size (%zu) exceeds "
00621          "the maximum payload space in a log buffer", m_basename, bytes_needed);
00622     return Log::FAIL;
00623   }
00624   
00625   
00626   
00627   
00628   
00629 
00630   if (lad && m_format->is_aggregate()) {
00631     
00632     
00633     bytes_used = m_format->m_field_list.marshal_agg(&(*buffer)[offset]);
00634     ink_assert(bytes_needed >= bytes_used);
00635     m_format->m_interval_next += m_format->m_interval_sec;
00636     Debug("log-agg", "Aggregate entry created; next time is %ld", m_format->m_interval_next);
00637   } else if (lad) {
00638     bytes_used = m_format->m_field_list.marshal(lad, &(*buffer)[offset]);
00639     ink_assert(bytes_needed >= bytes_used);
00640   } else if (text_entry) {
00641     ink_strlcpy(&(*buffer)[offset], text_entry, bytes_needed);
00642   }
00643 
00644   buffer->checkin_write(offset);
00645 
00646   return Log::LOG_OK;
00647 }
00648 
00649 
00650 void
00651 LogObject::_setup_rolling(Log::RollingEnabledValues rolling_enabled, int rolling_interval_sec, int rolling_offset_hr, int rolling_size_mb)
00652 {
00653   if (!LogRollingEnabledIsValid((int)rolling_enabled)) {
00654     m_rolling_enabled = Log::NO_ROLLING;
00655     m_rolling_interval_sec = 0;
00656     m_rolling_offset_hr = 0;
00657     m_rolling_size_mb = 0;
00658     if (rolling_enabled != Log::NO_ROLLING) {
00659       Warning("Valid rolling_enabled values are %d to %d, invalid value "
00660               "(%d) specified for %s, rolling will be disabled for this file.",
00661               Log::NO_ROLLING, Log::INVALID_ROLLING_VALUE - 1, rolling_enabled, m_filename);
00662     } else {
00663       Status("Rolling disabled for %s", m_filename);
00664     }
00665   } else {
00666     
00667     
00668     if (rolling_enabled == Log::ROLL_ON_TIME_ONLY ||
00669         rolling_enabled == Log::ROLL_ON_TIME_OR_SIZE || rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE) {
00670       if (rolling_interval_sec < Log::MIN_ROLLING_INTERVAL_SEC) {
00671         
00672         m_rolling_interval_sec = Log::MIN_ROLLING_INTERVAL_SEC;
00673       } else if (rolling_interval_sec > Log::MAX_ROLLING_INTERVAL_SEC) {
00674         
00675         m_rolling_interval_sec = Log::MAX_ROLLING_INTERVAL_SEC;
00676       } else if (Log::MAX_ROLLING_INTERVAL_SEC % rolling_interval_sec == 0) {
00677         
00678         m_rolling_interval_sec = rolling_interval_sec;
00679       } else {
00680         m_rolling_interval_sec = rolling_interval_sec;
00681         
00682         while (Log::MAX_ROLLING_INTERVAL_SEC % ++m_rolling_interval_sec);
00683       }
00684 
00685       if (m_rolling_interval_sec != rolling_interval_sec) {
00686         Note("Rolling interval adjusted from %d sec to %d sec for %s",
00687              rolling_interval_sec, m_rolling_interval_sec, m_filename);
00688       }
00689 
00690       if (rolling_offset_hr<0 || rolling_offset_hr> 23) {
00691         rolling_offset_hr = 0;
00692         Note("Rolling offset out of bounds for %s, setting it to %d", m_filename, rolling_offset_hr);
00693       }
00694 
00695       m_rolling_offset_hr = rolling_offset_hr;
00696       m_rolling_size_mb = 0; 
00697                              
00698     }
00699 
00700     if (rolling_enabled == Log::ROLL_ON_SIZE_ONLY ||
00701         rolling_enabled == Log::ROLL_ON_TIME_OR_SIZE || rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE) {
00702       if (rolling_size_mb < 10) {
00703         m_rolling_size_mb = 10;
00704         Note("Rolling size invalid(%d) for %s, setting it to 10 MB", rolling_size_mb, m_filename);
00705       } else {
00706         m_rolling_size_mb = rolling_size_mb;
00707       }
00708     }
00709 
00710     m_rolling_enabled = rolling_enabled;
00711   }
00712 }
00713 
00714 
00715 unsigned
00716 LogObject::roll_files(long time_now)
00717 {
00718   if (!m_rolling_enabled)
00719     return 0;
00720 
00721   unsigned num_rolled = 0;
00722   bool roll_on_time = false;
00723   bool roll_on_size = false;
00724 
00725   if (!time_now)
00726     time_now = LogUtils::timestamp();
00727 
00728   if (m_rolling_enabled != Log::ROLL_ON_SIZE_ONLY) {
00729     if (m_rolling_interval_sec > 0) {
00730       
00731       
00732       
00733       
00734       
00735       
00736       
00737       
00738       int secs_to_next = LogUtils::seconds_to_next_roll(time_now, m_rolling_offset_hr, m_rolling_interval_sec);
00739 
00740       
00741       
00742       
00743       
00744       
00745       
00746       int secs_since_last = (m_last_roll_time < time_now ? time_now - m_last_roll_time : m_last_roll_time - time_now);
00747 
00748       
00749       
00750       
00751       const int missed_window = 10;
00752 
00753       roll_on_time =
00754         ((secs_to_next == 0 ||
00755           secs_to_next >= m_rolling_interval_sec - missed_window) && secs_since_last > missed_window);
00756     }
00757   }
00758 
00759   if (m_rolling_enabled != Log::ROLL_ON_TIME_ONLY) {
00760     if (m_rolling_size_mb) {
00761       
00762       
00763       roll_on_size = (get_file_size_bytes() > m_rolling_size_mb * LOG_MEGABYTE);
00764     }
00765   }
00766 
00767   if ((roll_on_time && should_roll_on_time(m_rolling_enabled)) ||
00768       (roll_on_size && should_roll_on_size(m_rolling_enabled)) ||
00769       (roll_on_time && roll_on_size && m_rolling_enabled == Log::ROLL_ON_TIME_AND_SIZE)) {
00770     num_rolled = _roll_files(m_last_roll_time, time_now ? time_now : LogUtils::timestamp());
00771   }
00772 
00773   return num_rolled;
00774 }
00775 
00776 
00777 unsigned
00778 LogObject::_roll_files(long last_roll_time, long time_now)
00779 {
00780   unsigned num_rolled = 0;
00781 
00782   if (m_logFile) {
00783     
00784     if (!writes_to_pipe()) {
00785       num_rolled += m_logFile->roll(last_roll_time, time_now);
00786     }
00787   } else {
00788     LogHost *host;
00789     for (host = m_host_list.first(); host; host = m_host_list.next(host)) {
00790       LogFile *orphan_logfile = host->get_orphan_logfile();
00791       if (orphan_logfile) {
00792         num_rolled += orphan_logfile->roll(last_roll_time, time_now);
00793       }
00794     }
00795   }
00796   m_last_roll_time = time_now;
00797   return num_rolled;
00798 }
00799 
00800 
00801 void
00802 LogObject::check_buffer_expiration(long time_now)
00803 {
00804   LogBuffer *b = (LogBuffer*)FREELIST_POINTER(m_log_buffer);
00805   if (b && time_now > b->expiration_time()) {
00806     force_new_buffer();
00807   }
00808 }
00809 
00810 
00811 
00812 
00813 int
00814 LogObject::do_filesystem_checks()
00815 {
00816   if (m_logFile) {
00817     return m_logFile->do_filesystem_checks();
00818   } else {
00819     return m_host_list.do_filesystem_checks();
00820   }
00821 }
00822 
00823 
00824 
00825 
00826 
00827 const LogFormat * TextLogObject::textfmt = MakeTextLogFormat();
00828 
00829 TextLogObject::TextLogObject(const char *name, const char *log_dir,
00830                              bool timestamps, const char *header,
00831                              Log::RollingEnabledValues rolling_enabled, int flush_threads,
00832                              int rolling_interval_sec, int rolling_offset_hr,
00833                              int rolling_size_mb)
00834   : LogObject(TextLogObject::textfmt, log_dir, name, LOG_FILE_ASCII, header,
00835               rolling_enabled, flush_threads, rolling_interval_sec,
00836               rolling_offset_hr, rolling_size_mb)
00837 {
00838   if (timestamps) {
00839     this->set_fmt_timestamps();
00840   }
00841 }
00842 
00843 
00844 
00845 
00846 
00847 
00848 
00849 
00850 
00851 
00852 
00853 int
00854 TextLogObject::write(const char *format, ...)
00855 {
00856   int ret_val;
00857 
00858   ink_assert(format != NULL);
00859   va_list ap;
00860   va_start(ap, format);
00861   ret_val = va_write(format, ap);
00862   va_end(ap);
00863 
00864   return ret_val;
00865 }
00866 
00867 
00868 
00869 
00870 
00871 
00872 
00873 
00874 
00875 
00876 
00877 int
00878 TextLogObject::va_write(const char *format, va_list ap)
00879 {
00880   return this->va_log(NULL, format, ap);
00881 }
00882 
00883 
00884 
00885 
00886 
00887 
00888 LogObjectManager::LogObjectManager()
00889 {
00890   _APImutex = new ink_mutex;
00891   ink_mutex_init(_APImutex, "_APImutex");
00892 }
00893 
00894 LogObjectManager::~LogObjectManager()
00895 {
00896   for (unsigned i = 0; i < _objects.length(); ++i) {
00897     if (REF_COUNT_OBJ_REFCOUNT_DEC(_objects[i]) == 0) {
00898       delete _objects[i];
00899     }
00900   }
00901 
00902   for (unsigned i = 0; i < _APIobjects.length(); ++i) {
00903     if (REF_COUNT_OBJ_REFCOUNT_DEC(_APIobjects[i]) == 0) {
00904       delete _APIobjects[i];
00905     }
00906   }
00907 
00908   delete _APImutex;
00909 }
00910 
00911 int
00912 LogObjectManager::_manage_object(LogObject * log_object, bool is_api_object, int maxConflicts)
00913 {
00914   if (is_api_object) {
00915     ACQUIRE_API_MUTEX("A LogObjectManager::_manage_object");
00916   }
00917 
00918   bool col_client = log_object->is_collation_client();
00919   int retVal = _solve_internal_filename_conflicts(log_object, maxConflicts);
00920 
00921   if (retVal == NO_FILENAME_CONFLICTS) {
00922     
00923     
00924     
00925     if (col_client || (retVal = _solve_filename_conflicts(log_object, maxConflicts), retVal == NO_FILENAME_CONFLICTS)) {
00926 
00927       
00928       
00929       if (log_object->do_filesystem_checks() < 0) {
00930         const char *msg = "The log file %s did not pass filesystem checks. " "No output will be produced for this log";
00931         Error(msg, log_object->get_full_filename());
00932         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, log_object->get_full_filename());
00933         retVal = ERROR_DOING_FILESYSTEM_CHECKS;
00934 
00935       } else {
00936 
00937         
00938         
00939         REF_COUNT_OBJ_REFCOUNT_INC(log_object);
00940         if (is_api_object) {
00941           _APIobjects.push_back(log_object);
00942         } else {
00943           _objects.push_back(log_object);
00944         }
00945 
00946         ink_release_assert(retVal == NO_FILENAME_CONFLICTS);
00947 
00948         Debug("log", "LogObjectManager managing object %s (%s) "
00949               "[signature = %" PRIu64 ", address = %p]",
00950               log_object->get_base_filename(),
00951               col_client ? "collation client" :
00952               log_object->get_full_filename(), log_object->get_signature(), log_object);
00953 
00954         if (log_object->has_alternate_name()) {
00955           Warning("The full path for the (%s) LogObject %s "
00956                   "with signature %" PRIu64 " "
00957                   "has been set to %s rather than %s because the latter "
00958                   "is being used by another LogObject",
00959                   log_object->receives_remote_data()? "remote" : "local",
00960                   log_object->get_base_filename(),
00961                   log_object->get_signature(), log_object->get_full_filename(), log_object->get_original_filename());
00962         }
00963       }
00964     }
00965   }
00966 
00967   if (is_api_object) {
00968     RELEASE_API_MUTEX("R LogObjectManager::_manage_object");
00969   }
00970 
00971   return retVal;
00972 }
00973 
00974 
00975 int
00976 LogObjectManager::_solve_filename_conflicts(LogObject * log_object, int maxConflicts)
00977 {
00978   int retVal = NO_FILENAME_CONFLICTS;
00979 
00980   const char *filename = log_object->get_full_filename();
00981 
00982   if (access(filename, F_OK)) {
00983     if (errno != ENOENT) {
00984       const char *msg = "Cannot access log file %s: %s";
00985       const char *se = strerror(errno);
00986 
00987       Error(msg, filename, se);
00988       LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename, se);
00989       retVal = ERROR_ACCESSING_LOG_FILE;
00990     }
00991   } else {
00992     
00993     
00994     uint64_t signature = 0;
00995     MetaInfo meta_info(filename);
00996     bool conflicts = true;
00997 
00998     if (meta_info.file_open_successful()) {
00999       bool got_sig = meta_info.get_log_object_signature(&signature);
01000       uint64_t obj_sig = log_object->get_signature();
01001 
01002       if (got_sig && signature == obj_sig) {
01003         conflicts = false;
01004       }
01005       Debug("log", "LogObjectManager::_solve_filename_conflicts\n"
01006             "\tfilename = %s\n"
01007             "\tmeta file signature = %" PRIu64 "\n"
01008             "\tlog object signature = %" PRIu64 "\n" "\tconflicts = %d", filename, signature, obj_sig, conflicts);
01009     }
01010 
01011     if (conflicts) {
01012       if (maxConflicts == 0) {
01013         
01014         
01015         const char *msg = "Cannot solve filename conflicts for log file %s";
01016 
01017         Error(msg, filename);
01018         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename);
01019         retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
01020       } else {
01021         
01022         
01023         
01024         
01025         
01026         
01027 
01028         bool roll_file = true;
01029 
01030         if (log_object->writes_to_pipe()) {
01031           
01032           
01033           
01034           
01035           struct stat s;
01036           if (stat(filename, &s) < 0) {
01037             
01038             
01039             const char *msg = "Cannot stat log file %s: %s";
01040             char *se = strerror(errno);
01041 
01042             Error(msg, filename, se);
01043             LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename, se);
01044             retVal = ERROR_DETERMINING_FILE_INFO;
01045             roll_file = false;
01046           } else {
01047             if (s.st_mode == S_IFIFO) {
01048               unlink(filename);
01049               roll_file = false;
01050             }
01051           }
01052         }
01053         if (roll_file) {
01054           Warning("File %s will be rolled because a LogObject with "
01055                   "different format is requesting the same " "filename", filename);
01056           LogFile logfile(filename, NULL, LOG_FILE_ASCII, 0);
01057           long time_now = LogUtils::timestamp();
01058 
01059           if (logfile.roll(time_now - log_object->get_rolling_interval(), time_now) == 0) {
01060             
01061             
01062             const char *msg = "Cannot roll log file %s to fix log " "filename conflicts";
01063 
01064             Error(msg, filename);
01065             LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename);
01066             retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
01067 
01068           }
01069         }
01070       }
01071     }
01072   }
01073   return retVal;
01074 }
01075 
01076 
01077 bool
01078 LogObjectManager::_has_internal_filename_conflict(const char *filename, LogObjectList& objects)
01079 {
01080   for (unsigned i = 0; i < objects.length(); i++) {
01081 
01082     if (!objects[i]->is_collation_client()) {
01083       
01084       
01085       
01086       
01087       if (strcmp(objects[i]->get_full_filename(), filename) == 0) {
01088         return true;
01089       }
01090     }
01091   }
01092   return false;
01093 }
01094 
01095 
01096 int
01097 LogObjectManager::_solve_internal_filename_conflicts(LogObject *log_object, int maxConflicts, int fileNum)
01098 {
01099   int retVal = NO_FILENAME_CONFLICTS;
01100   const char *filename = log_object->get_full_filename();
01101 
01102   if (_has_internal_filename_conflict(filename, _objects) ||
01103       _has_internal_filename_conflict(filename, _APIobjects)) {
01104     if (fileNum < maxConflicts) {
01105       char new_name[MAXPATHLEN];
01106 
01107       snprintf(new_name, sizeof(new_name), "%s%s%d",
01108                log_object->get_original_filename(), LOGFILE_SEPARATOR_STRING, ++fileNum);
01109       log_object->rename(new_name);
01110       retVal = _solve_internal_filename_conflicts(log_object, maxConflicts, fileNum);
01111     } else {
01112       const char *msg = "Cannot solve filename conflicts for log file %s";
01113 
01114       Error(msg, filename);
01115       LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, filename);
01116       retVal = CANNOT_SOLVE_FILENAME_CONFLICTS;
01117     }
01118   }
01119   return retVal;
01120 }
01121 
01122 
01123 LogObject *
01124 LogObjectManager::get_object_with_signature(uint64_t signature)
01125 {
01126   for (unsigned i = 0; i < this->_objects.length(); i++) {
01127     LogObject * obj = this->_objects[i];
01128 
01129     if (obj->get_signature() == signature) {
01130       return obj;
01131     }
01132   }
01133   return NULL;
01134 }
01135 
01136 
01137 void
01138 LogObjectManager::check_buffer_expiration(long time_now)
01139 {
01140   for (unsigned i = 0; i < this->_objects.length(); i++) {
01141     this->_objects[i]->check_buffer_expiration(time_now);
01142   }
01143 
01144   ACQUIRE_API_MUTEX("A LogObjectManager::check_buffer_expiration");
01145 
01146   for (unsigned i = 0; i < this->_APIobjects.length(); i++) {
01147     this->_APIobjects[i]->check_buffer_expiration(time_now);
01148   }
01149 
01150   RELEASE_API_MUTEX("R LogObjectManager::check_buffer_expiration");
01151 }
01152 
01153 size_t
01154 LogObjectManager::preproc_buffers(int idx)
01155 {
01156   size_t buffers_preproced = 0;
01157 
01158   for (unsigned i = 0; i < this->_objects.length(); i++) {
01159     buffers_preproced += this->_objects[i]->preproc_buffers(idx);
01160   }
01161 
01162   ACQUIRE_API_MUTEX("A LogObjectManager::preproc_buffers");
01163 
01164   for (unsigned i = 0; i < this->_APIobjects.length(); i++) {
01165     buffers_preproced += this->_APIobjects[i]->preproc_buffers(idx);
01166   }
01167 
01168   RELEASE_API_MUTEX("R LogObjectManager::preproc_buffers");
01169 
01170   return buffers_preproced;
01171 }
01172 
01173 
01174 bool
01175 LogObjectManager::unmanage_api_object(LogObject * logObject)
01176 {
01177   ACQUIRE_API_MUTEX("A LogObjectManager::unmanage_api_object");
01178 
01179   if (this->_APIobjects.in(logObject)) {
01180     this->_APIobjects.remove(logObject);
01181 
01182     
01183     logObject->force_new_buffer();
01184     new_Derefer(logObject, HRTIME_SECONDS(60));
01185 
01186     RELEASE_API_MUTEX("R LogObjectManager::unmanage_api_object");
01187     return true;
01188   }
01189 
01190   RELEASE_API_MUTEX("R LogObjectManager::unmanage_api_object");
01191   return false;
01192 }
01193 
01194 
01195 void
01196 LogObjectManager::add_filter_to_all(LogFilter * filter)
01197 {
01198   for (unsigned i = 0; i < this->_objects.length(); i++) {
01199     _objects[i]->add_filter(filter);
01200   }
01201 }
01202 
01203 
01204 void
01205 LogObjectManager::open_local_pipes()
01206 {
01207   
01208   
01209   
01210   for (unsigned i = 0; i < this->_objects.length(); i++) {
01211     LogObject *obj = _objects[i];
01212     if (obj->writes_to_pipe() && !obj->is_collation_client()) {
01213       obj->m_logFile->open_file();
01214     }
01215   }
01216 }
01217 
01218 
01219 void
01220 LogObjectManager::transfer_objects(LogObjectManager & old_mgr)
01221 {
01222   unsigned num_kept_objects = 0;
01223 
01224   Debug("log-config-transfer", "transferring objects from LogObjectManager %p, to %p", &old_mgr, this);
01225 
01226   if (is_debug_tag_set("log-config-transfer")) {
01227     Debug("log-config-transfer", "TRANSFER OBJECTS: list of old objects");
01228     for (unsigned i = 0; i < old_mgr._objects.length(); i++) {
01229       Debug("log-config-transfer", "%s", old_mgr._objects[i]->get_original_filename());
01230     }
01231 
01232     Debug("log-config-transfer", "TRANSFER OBJECTS : list of new objects");
01233     for (unsigned i = 0; i < this->_objects.length(); i++) {
01234       Debug("log-config-transfer", "%s", _objects[i]->get_original_filename());
01235     }
01236   }
01237 
01238   
01239   for (unsigned i = 0; i < old_mgr._APIobjects.length(); ++i) {
01240     manage_api_object(old_mgr._APIobjects[i]);
01241   }
01242 
01243   for (unsigned i = 0; i < old_mgr._objects.length(); ++i) {
01244     LogObject * old_obj = old_mgr._objects[i];
01245     LogObject * new_obj;
01246 
01247     Debug("log-config-transfer", "examining existing object %s", old_obj->get_base_filename());
01248 
01249     
01250     
01251     
01252     for (unsigned j = 0; j < _objects.length(); j++) {
01253       new_obj = _objects[j];
01254 
01255       Debug("log-config-transfer",
01256             "comparing existing object %s to new object %s", old_obj->get_base_filename(), new_obj->get_base_filename());
01257 
01258       if (*new_obj == *old_obj) {
01259         Debug("log-config-transfer", "keeping existing object %s", old_obj->get_base_filename());
01260 
01261         REF_COUNT_OBJ_REFCOUNT_INC(old_obj);
01262         this->_objects[j] = old_obj;
01263 
01264         if (REF_COUNT_OBJ_REFCOUNT_DEC(new_obj) == 0) {
01265           delete new_obj;
01266         }
01267         ++num_kept_objects;
01268         break;
01269       }
01270     }
01271   }
01272 
01273   if (is_debug_tag_set("log-config-transfer")) {
01274     Debug("log-config-transfer", "Log Object List after transfer:");
01275     display();
01276   }
01277 }
01278 
01279 unsigned
01280 LogObjectManager::roll_files(long time_now)
01281 {
01282   int num_rolled = 0;
01283 
01284   for (unsigned i = 0; i < this->_objects.length(); i++) {
01285     num_rolled += this->_objects[i]->roll_files(time_now);
01286   }
01287 
01288   ACQUIRE_API_MUTEX("A LogObjectManager::roll_files");
01289 
01290   for (unsigned i = 0; i < this->_APIobjects.length(); i++) {
01291     num_rolled += this->_APIobjects[i]->roll_files(time_now);
01292   }
01293 
01294   RELEASE_API_MUTEX("R LogObjectManager::roll_files");
01295 
01296   return num_rolled;
01297 }
01298 
01299 void
01300 LogObjectManager::display(FILE * str)
01301 {
01302   for (unsigned i = 0; i < this->_objects.length(); i++) {
01303     _objects[i]->display(str);
01304   }
01305 
01306   ACQUIRE_API_MUTEX("A LogObjectManager::display");
01307   for (unsigned i = 0; i < this->_APIobjects.length(); i++) {
01308     _APIobjects[i]->display(str);
01309   }
01310   RELEASE_API_MUTEX("R LogObjectManager::display");
01311 }
01312 
01313 LogObject *
01314 LogObjectManager::find_by_format_name(const char *name) const
01315 {
01316   for (unsigned i = 0; i < this->_objects.length(); ++i) {
01317     if (this->_objects[i] && this->_objects[i]->m_format->name_id() == LogFormat::id_from_name(name)) {
01318       return this->_objects[i];
01319     }
01320   }
01321   return NULL;
01322 }
01323 
01324 unsigned
01325 LogObjectManager::get_num_collation_clients() const
01326 {
01327   unsigned coll_clients = 0;
01328 
01329   for (unsigned i = 0; i < this->_objects.length(); ++i) {
01330     if (this->_objects[i] && this->_objects[i]->is_collation_client()) {
01331       ++coll_clients;
01332     }
01333   }
01334   return coll_clients;
01335 }
01336 
01337 int
01338 LogObjectManager::log(LogAccess * lad)
01339 {
01340   int ret = Log::SKIP;
01341   ProxyMutex *mutex = this_thread()->mutex;
01342 
01343   for (unsigned i = 0; i < this->_objects.length(); i++) {
01344     
01345     
01346     
01347     
01348     
01349     if (_objects[i]->m_auto_created)
01350       continue;
01351 
01352     ret |= _objects[i]->log(lad);
01353   }
01354 
01355   
01356   
01357   
01358   
01359   
01360   if (unlikely(ret & Log::FAIL)) {
01361     RecIncrRawStat(log_rsb, mutex->thread_holding,
01362                    log_stat_event_log_access_fail_stat, 1);
01363   } else if (unlikely(ret & Log::FULL)) {
01364     RecIncrRawStat(log_rsb, mutex->thread_holding,
01365                    log_stat_event_log_access_full_stat, 1);
01366   } else if (likely(ret & Log::LOG_OK)) {
01367     RecIncrRawStat(log_rsb, mutex->thread_holding,
01368                    log_stat_event_log_access_ok_stat, 1);
01369   } else if (unlikely(ret & Log::AGGR)) {
01370     RecIncrRawStat(log_rsb, mutex->thread_holding,
01371                    log_stat_event_log_access_aggr_stat, 1);
01372   } else if (likely(ret & Log::SKIP)) {
01373     RecIncrRawStat(log_rsb, mutex->thread_holding,
01374                    log_stat_event_log_access_skip_stat, 1);
01375   } else {
01376     ink_release_assert("Unexpected result");
01377   }
01378 
01379   return ret;
01380 }
01381 
01382 void
01383 LogObjectManager::flush_all_objects()
01384 {
01385   for (unsigned i = 0; i < this->_objects.length(); ++i) {
01386     this->_objects[i]->force_new_buffer();
01387   }
01388 
01389   ACQUIRE_API_MUTEX("A LogObjectManager::flush_all_objects");
01390 
01391   for (unsigned i = 0; i < this->_APIobjects.length(); ++i) {
01392     this->_APIobjects[i]->force_new_buffer();
01393   }
01394 
01395   RELEASE_API_MUTEX("R LogObjectManager::flush_all_objects");
01396 }
01397 
01398 #if TS_HAS_TESTS
01399 
01400 static LogObject *
01401 MakeTestLogObject(const char * name)
01402 {
01403   const char * tmpdir = getenv("TMPDIR");
01404   LogFormat format("testfmt", NULL);
01405 
01406   if (!tmpdir) {
01407     tmpdir = "/tmp";
01408   }
01409 
01410   return new LogObject(&format, tmpdir, name,
01411                        LOG_FILE_ASCII , name ,
01412                        Log::ROLL_ON_TIME_ONLY , 1 );
01413 }
01414 
01415 REGRESSION_TEST(LogObjectManager_Transfer)(RegressionTest * t, int , int * pstatus)
01416 {
01417   TestBox box(t, pstatus);
01418 
01419   
01420   
01421   
01422   {
01423     LogObjectManager mgr1;
01424     LogObjectManager mgr2;
01425 
01426     mgr1.manage_object(MakeTestLogObject("object1"));
01427     mgr1.manage_object(MakeTestLogObject("object2"));
01428     mgr1.manage_object(MakeTestLogObject("object3"));
01429     mgr1.manage_object(MakeTestLogObject("object4"));
01430 
01431     mgr2.transfer_objects(mgr1);
01432 
01433     rprintf(t, "mgr1 has %d objects, mgr2 has %d objects\n",
01434         (int)mgr1.get_num_objects(), (int)mgr2.get_num_objects());
01435 
01436     rprintf(t, "running Log::periodoc_tasks()\n");
01437     Log::periodic_tasks(ink_get_hrtime() / HRTIME_SECOND);
01438     rprintf(t, "Log::periodoc_tasks() done\n");
01439   }
01440 
01441   box = REGRESSION_TEST_PASSED;
01442 }
01443 
01444 #endif