• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

LogObject.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /***************************************************************************
00025  LogObject.cc
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       // Still has outstanding references.
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   LogObject
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     // compute_signature is a static function
00118     m_signature = compute_signature(m_format, m_basename, m_flags);
00119 
00120     // by default, create a LogFile for this object, if a loghost is
00121     // later specified, then we will delete the LogFile object
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     // copy gets a fresh log buffer
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   // here we need to free LogHost if it is remote logging.
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 // This function generates an object filename according to the following rules:
00202 //
00203 // 1.- if no extension is given, add .log for ascii logs, and .blog for
00204 //     binary logs
00205 // 2.- if an extension is given, then do not modify filename and use that
00206 //     extension regardless of type of log
00207 // 3.- if there is a '.' at the end of the name, then do not add an extension
00208 //     and remove the '.'. To have a dot at the end of the filename, specify
00209 //     two ('..').
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   };                            // remove dot at end of name
00227 
00228   const char *ext = 0;
00229   int ext_len = 0;
00230   if (i < 0) {                  // no extension, add one
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; // include null terminator
00251   int total_len = dir_len + 1 + basename_len;   // include '/'
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   // NOTE: this function is intended to be called by the LogObjectManager
00275   // while solving filename conflicts. It DOES NOT modify the signature of
00276   // the LogObject to match the new filename.
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   // A LogObject either writes to a file, or sends to a collation host, but
00316   // not both. By default, it writes to a file. If a LogHost is specified,
00317   // then clear the intelligent Ptr containing LogFile.
00318   //
00319   m_logFile.clear();
00320 }
00321 
00322 
00323 // we conpute the object signature from the fieldlist_str and the printf_str
00324 // of the LogFormat rather than from the format_str because the format_str
00325 // is not part of a LogBuffer header
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     // display format and filter objects
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     // To avoid a race condition, we keep a count of held references in
00405     // the pointer itself and add this to m_outstanding_references.
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       // checkout succeded
00425       //
00426       retry = false;
00427       break;
00428 
00429     case LogBuffer::LB_FULL_ACTIVE_WRITERS:
00430     case LogBuffer::LB_FULL_NO_WRITERS:
00431       // no more room in current buffer, create a new one
00432       new_buffer = new LogBuffer(this, Log::config->log_buffer_size);
00433 
00434       // swap the new buffer for the old one
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           // another thread should be taking care of creating a new
00443           // buffer, so delete new_buffer and try again
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       // no more room, but another thread should be taking care of
00469       // creating a new buffer, so try again
00470       //
00471       break;
00472 
00473     case LogBuffer::LB_BUFFER_TOO_SMALL:
00474 
00475       // return a null buffer to signal the caller that this
00476       // transaction cannot be logged
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);      // if write_offset is null, we do
00503   // not retry because we really do
00504   // not want to write to the buffer
00505   // only to set it as full
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; // 16K? Really?
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     // Add a space after the timestamp
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   // Now that we have an entry and it's length (len), we can place it
00538   // into the associated logbuffer.
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;            // prevent warning
00547   size_t bytes_needed = 0, bytes_used = 0;
00548 
00549   // log to a pipe even if space is exhausted since pipe uses no space
00550   // likewise, send data to a remote client even if local space is exhausted
00551   // (if there is a remote client, m_logFile will be NULL
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   // this verification must be done here in order to avoid 'dead' LogBuffers
00557   // with none zero 'in usage' counters (see _checkout_write for more details)
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     // marshal the field data into the temp space provided by the
00574     // LogFormat object for aggregate formats
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     // step through each of the fields and update the LogField object
00584     // with the newly-marshalled data
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       // convert to host order to do computations
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     // can easily compute bytes_needed because all fields are INTs
00603     // and will use INK_MIN_ALIGN each
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   // Now try to place this entry in the current LogBuffer.
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   // Ok, the checkout_write was successful, which means we have a valid
00626   // offset into the current buffer.  Marshal the entry into the buffer,
00627   // and the commit (checkin) the changes.
00628   //
00629 
00630   if (lad && m_format->is_aggregate()) {
00631     // the "real" entry data is contained in the LogField objects
00632     // themselves, not in this lad.
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     // do checks for rolling based on time
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         // check minimum
00672         m_rolling_interval_sec = Log::MIN_ROLLING_INTERVAL_SEC;
00673       } else if (rolling_interval_sec > Log::MAX_ROLLING_INTERVAL_SEC) {
00674         // 1 day maximum
00675         m_rolling_interval_sec = Log::MAX_ROLLING_INTERVAL_SEC;
00676       } else if (Log::MAX_ROLLING_INTERVAL_SEC % rolling_interval_sec == 0) {
00677         // OK, divides day evenly
00678         m_rolling_interval_sec = rolling_interval_sec;
00679       } else {
00680         m_rolling_interval_sec = rolling_interval_sec;
00681         // increase so it divides day evenly
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; // it is safe to set it as 0, if we set SIZE rolling,
00697                              // it will be updated later
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       // We make no assumptions about the current time not having
00731       // changed underneath us. This could happen during daylight
00732       // savings time adjustments, or if time is adjusted via NTP.
00733       //
00734       // For this reason we don't cache the number of seconds
00735       // remaining until the next roll, but we calculate this figure
00736       // every time ...
00737       //
00738       int secs_to_next = LogUtils::seconds_to_next_roll(time_now, m_rolling_offset_hr, m_rolling_interval_sec);
00739 
00740       // ... likewise, we make sure we compute the absolute value
00741       // of the seconds since the last roll (which would otherwise
00742       // be negative if time "went back"). We will use this value
00743       // to make sure we don't roll twice if time goes back shortly
00744       // after rolling.
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       // number of seconds we allow for periodic_tasks() not to be
00749       // called and still be able to roll
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       // Get file size and check if the file size if greater than the
00762       // configured file size for rolling
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     // no need to roll if object writes to a pipe
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 // make sure that we will be able to write the logs to the disk
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   TextLogObject::TextLogObject
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   TextLogObject::write
00846 
00847   This routine will take a printf-style format string and variable number
00848   of arguments, and write them to the text file.
00849 
00850   It really just creates a va_list and calls va_write to do the work.
00851   Returns the number of bytes written to the file.
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   TextLogObject::va_write
00870 
00871   This routine will take a format string and va_list and write it as a
00872   single entry (line) in the text file.  If timestamps are on, then the
00873   entry will be preceeded by a timestamp.
00874 
00875   Returns ReturnCodeFlags.
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   LogObjectManager
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     // check for external conflicts only if the object is not a collation
00923     // client
00924     //
00925     if (col_client || (retVal = _solve_filename_conflicts(log_object, maxConflicts), retVal == NO_FILENAME_CONFLICTS)) {
00926 
00927       // do filesystem checks
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         // no conflicts, add object to the list of managed objects
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     // file exists, try to read metafile to get object signature
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         // do not take any action, and return an error status
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         // either the meta file could not be read, or the new object's
01022         // signature and the metafile signature do not match ==>
01023         // roll old filename so the new object can use the filename
01024         // it requested (previously we used to rename the NEW file
01025         // but now we roll the OLD file), or if the log object writes
01026         // to a pipe, just remove the file if it was open as a pipe
01027 
01028         bool roll_file = true;
01029 
01030         if (log_object->writes_to_pipe()) {
01031           // determine if existing file is a pipe, and remove it if
01032           // that is the case so the right metadata for the new pipe
01033           // is created later
01034           //
01035           struct stat s;
01036           if (stat(filename, &s) < 0) {
01037             // an error happened while trying to get file info
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             // an error happened while trying to roll the file
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       // an internal conflict exists if two objects request the
01084       // same filename, regardless of the object signatures, since
01085       // two objects writing to the same file would produce a
01086       // log with duplicate entries and non monotonic timestamps
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     // Force a buffer flush, then schedule this LogObject to be deleted on the eventProcessor.
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   // for all local objects that write to a pipe, call open_file to force
01208   // the creation of the pipe so that any potential reader can see it
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   // Transfer the API objects from the old manager. The old manager will retain its refcount.
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     // See if any of the new objects is just a copy of an old one. If so, transfer the
01250     // old one to the new manager and delete the new one. We don't use Vec::in here because
01251     // we need to compare the object hash, not the pointers.
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     // Auto created LogObject is only applied to LogBuffer
01346     // data received from network in collation host. It should
01347     // be ignored here.
01348     //
01349     if (_objects[i]->m_auto_created)
01350       continue;
01351 
01352     ret |= _objects[i]->log(lad);
01353   }
01354 
01355   //
01356   // The bit-field code in *ret* are priority chain:
01357   // FAIL > FULL > LOG_OK > AGGR > SKIP
01358   // The if-statement should keep step with the priority order.
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 /* file_format */, name /* header */,
01412                        Log::ROLL_ON_TIME_ONLY /* rolling_enabled */, 1 /* flush_threads */);
01413 }
01414 
01415 REGRESSION_TEST(LogObjectManager_Transfer)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
01416 {
01417   TestBox box(t, pstatus);
01418 
01419   // There used to be a lot of confusion around whether LogObjects were owned by ome or more LogObjectManager
01420   // objects, or handed off to static storage in the Log class. This test just verifies that this is no longer
01421   // the case.
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

Generated by  doxygen 1.7.1