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

LogFile.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  LogFile.cc
00026 
00027 
00028  ***************************************************************************/
00029 
00030 #include "libts.h"
00031 
00032 #include <errno.h>
00033 #include <sys/types.h>
00034 #include <sys/stat.h>
00035 #include <fcntl.h>
00036 
00037 #include "Error.h"
00038 
00039 #include "P_EventSystem.h"
00040 #include "I_Machine.h"
00041 #include "LogSock.h"
00042 
00043 #include "LogField.h"
00044 #include "LogFilter.h"
00045 #include "LogFormat.h"
00046 #include "LogBuffer.h"
00047 #include "LogFile.h"
00048 #include "LogHost.h"
00049 #include "LogObject.h"
00050 #include "LogUtils.h"
00051 #include "LogConfig.h"
00052 #include "Log.h"
00053 
00054 /*-------------------------------------------------------------------------
00055   LogFile::LogFile
00056 
00057   This constructor builds a LogFile object given the path, filename, header,
00058   and logfile format type.  This is the common way to create a new logfile.
00059   -------------------------------------------------------------------------*/
00060 
00061 LogFile::LogFile(const char *name, const char *header, LogFileFormat format,
00062                  uint64_t signature, size_t ascii_buffer_size, size_t max_line_size)
00063   : m_file_format(format),
00064     m_name(ats_strdup(name)),
00065     m_header(ats_strdup(header)),
00066     m_signature(signature),
00067     m_meta_info(NULL),
00068     m_max_line_size(max_line_size)
00069 {
00070   delete m_meta_info;
00071   m_meta_info = NULL;
00072   m_fd = -1;
00073   m_start_time = 0L;
00074   m_end_time = 0L;
00075   m_bytes_written = 0;
00076   m_size_bytes = 0;
00077   m_ascii_buffer_size = (ascii_buffer_size < max_line_size ? max_line_size : ascii_buffer_size);
00078 
00079   Debug("log-file", "exiting LogFile constructor, m_name=%s, this=%p", m_name, this);
00080 }
00081 
00082 /*-------------------------------------------------------------------------
00083   LogFile::LogFile
00084 
00085   This (copy) contructor builds a LogFile object from another LogFile object.
00086   -------------------------------------------------------------------------*/
00087 
00088 LogFile::LogFile (const LogFile& copy)
00089   : m_file_format (copy.m_file_format),
00090     m_name  (ats_strdup (copy.m_name)),
00091     m_header  (ats_strdup (copy.m_header)),
00092     m_signature (copy.m_signature),
00093     m_meta_info (NULL),
00094     m_ascii_buffer_size (copy.m_ascii_buffer_size),
00095     m_max_line_size (copy.m_max_line_size),
00096     m_fd (-1),
00097     m_start_time (0L),
00098     m_end_time (0L),
00099     m_bytes_written (0)
00100 {
00101     ink_release_assert(m_ascii_buffer_size >= m_max_line_size);
00102 
00103     Debug("log-file", "exiting LogFile copy constructor, m_name=%s, this=%p",
00104           m_name, this);
00105 }
00106 /*-------------------------------------------------------------------------
00107   LogFile::~LogFile
00108   -------------------------------------------------------------------------*/
00109 
00110 LogFile::~LogFile()
00111 {
00112   Debug("log-file", "entering LogFile destructor, this=%p", this);
00113   close_file();
00114 
00115   ats_free(m_name);
00116   ats_free(m_header);
00117   delete m_meta_info;
00118   Debug("log-file", "exiting LogFile destructor, this=%p", this);
00119 }
00120 
00121 /*-------------------------------------------------------------------------
00122   LogFile::exists
00123 
00124   Returns true if the logfile already exists; false otherwise.
00125   -------------------------------------------------------------------------*/
00126 
00127 bool LogFile::exists(const char *pathname)
00128 {
00129   ink_assert(pathname != NULL);
00130   return (pathname && ::access(pathname, F_OK) == 0);
00131 }
00132 
00133 /*-------------------------------------------------------------------------
00134   LogFile::change_name
00135   -------------------------------------------------------------------------*/
00136 
00137 void
00138 LogFile::change_name(const char *new_name)
00139 {
00140   ats_free(m_name);
00141   m_name = ats_strdup(new_name);
00142 }
00143 
00144 /*-------------------------------------------------------------------------
00145   LogFile::change_header
00146   -------------------------------------------------------------------------*/
00147 
00148 void
00149 LogFile::change_header(const char *header)
00150 {
00151   ats_free(m_header);
00152   m_header = ats_strdup(header);
00153 }
00154 
00155 /*-------------------------------------------------------------------------
00156   LogFile::open
00157 
00158   Open the logfile for append access.  This will create a logfile if the
00159   file does not already exist.
00160   -------------------------------------------------------------------------*/
00161 
00162 int
00163 LogFile::open_file()
00164 {
00165   if (is_open()) {
00166     return LOG_FILE_NO_ERROR;
00167   }
00168 
00169   if (m_name && !strcmp(m_name, "stdout")) {
00170     m_fd = STDOUT_FILENO;
00171     return LOG_FILE_NO_ERROR;
00172   }
00173   //
00174   // Check to see if the file exists BEFORE we try to open it, since
00175   // opening it will also create it.
00176   //
00177   bool file_exists = LogFile::exists(m_name);
00178 
00179   if (file_exists) {
00180     if (!m_meta_info) {
00181       // This object must be fresh since it has not built its MetaInfo
00182       // so we create a new MetaInfo object that will read right away
00183       // (in the constructor) the corresponding metafile
00184       //
00185       m_meta_info = new MetaInfo(m_name);
00186     }
00187   } else {
00188     // The log file does not exist, so we create a new MetaInfo object
00189     //  which will save itself to disk right away (in the constructor)
00190     m_meta_info = new MetaInfo(m_name, LogUtils::timestamp(), m_signature);
00191   }
00192 
00193   int flags, perms;
00194 
00195   if (m_file_format == LOG_FILE_PIPE) {
00196     if (mkfifo(m_name, S_IRUSR | S_IWUSR) < 0) {
00197       if (errno != EEXIST) {
00198         Error("Could not create named pipe %s for logging: %s", m_name, strerror(errno));
00199         return LOG_FILE_COULD_NOT_CREATE_PIPE;
00200       }
00201     } else {
00202       Debug("log-file", "Created named pipe %s for logging", m_name);
00203     }
00204     flags = O_WRONLY | O_NDELAY;
00205     perms = 0;
00206   } else {
00207     flags = O_WRONLY | O_APPEND | O_CREAT;
00208     perms = Log::config->logfile_perm;
00209   }
00210 
00211   Debug("log-file", "attempting to open %s", m_name);
00212   m_fd =::open(m_name, flags, perms);
00213 
00214   if (m_fd < 0) {
00215     // if error happened because no process is reading the pipe don't
00216     // complain, otherwise issue an error message
00217     //
00218     if (errno != ENXIO) {
00219       Error("Error opening log file %s: %s", m_name, strerror(errno));
00220       return LOG_FILE_COULD_NOT_OPEN_FILE;
00221     }
00222     Debug("log-file", "no readers for pipe %s", m_name);
00223     return LOG_FILE_NO_PIPE_READERS;
00224   }
00225 
00226   int e = do_filesystem_checks();
00227   if (e != 0) {
00228     m_fd = -1;                  // reset to error condition
00229     return LOG_FILE_FILESYSTEM_CHECKS_FAILED;
00230   }
00231 
00232   // set m_bytes_written to force the rolling based on filesize.
00233   m_bytes_written = lseek( m_fd, 0, SEEK_CUR );
00234 
00235   Debug("log-file", "LogFile %s is now open (fd=%d)", m_name, m_fd);
00236 
00237   //
00238   // If we've opened the file and it didn't already exist, then this is a
00239   // "new" file and we need to make some initializations.  This is the
00240   // time to write any headers and do any one-time initialization of the
00241   // file.
00242   //
00243   if (!file_exists) {
00244     if (m_file_format != LOG_FILE_BINARY && m_header != NULL) {
00245       Debug("log-file", "writing header to LogFile %s", m_name);
00246       writeln(m_header, strlen(m_header), m_fd, m_name);
00247     }
00248   }
00249 
00250   RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding,
00251                  log_stat_log_files_open_stat, 1);
00252 
00253   return LOG_FILE_NO_ERROR;
00254 }
00255 
00256 /*-------------------------------------------------------------------------
00257   LogFile::close
00258 
00259   Close the current logfile.
00260   -------------------------------------------------------------------------*/
00261 
00262 void
00263 LogFile::close_file()
00264 {
00265   if (is_open()) {
00266     ::close(m_fd);
00267     Debug("log-file", "LogFile %s (fd=%d) is closed", m_name, m_fd);
00268     m_fd = -1;
00269 
00270     RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding,
00271                    log_stat_log_files_open_stat, -1);
00272   }
00273 }
00274 
00275 /*-------------------------------------------------------------------------
00276   LogFile::rolled_logfile
00277 
00278   This function will return true if the given filename corresponds to a
00279   rolled logfile.  We make this determination based on the file extension.
00280   -------------------------------------------------------------------------*/
00281 
00282 bool LogFile::rolled_logfile(char *path)
00283 {
00284   const int
00285     target_len = (int) strlen(LOGFILE_ROLLED_EXTENSION);
00286   int
00287     len = (int) strlen(path);
00288   if (len > target_len) {
00289     char *
00290       str = &path[len - target_len];
00291     if (!strcmp(str, LOGFILE_ROLLED_EXTENSION)) {
00292       return true;
00293     }
00294   }
00295   return false;
00296 }
00297 
00298 /*-------------------------------------------------------------------------
00299   LogFile::roll
00300 
00301   This function is called by a LogObject to roll its files.  The
00302   tricky part to this routine is in coming up with the new file name,
00303   which contains the bounding timestamp interval for the entries
00304   within the file.
00305 
00306   Under normal operating conditions, this LogFile object was in existence
00307   for all writes to the file.  In this case, the LogFile members m_start_time
00308   and m_end_time will have the starting and ending times for the actual
00309   entries written to the file.
00310 
00311   On restart situations, it is possible to re-open an existing logfile,
00312   which means that the m_start_time variable will be later than the actual
00313   entries recorded in the file.  In this case, we'll use the creation time
00314   of the file, which should be recorded in the meta-information located on
00315   disk.
00316 
00317   If we can't use the meta-file, either because it's not there or because
00318   it's not valid, then we'll use timestamp 0 (Jan 1, 1970) as the starting
00319   bound.
00320 
00321   Return 1 if file rolled, 0 otherwise
00322   -------------------------------------------------------------------------*/
00323 
00324 int
00325 LogFile::roll(long interval_start, long interval_end)
00326 {
00327   //
00328   // First, let's see if a roll is even needed.
00329   //
00330   if (m_name == NULL || !LogFile::exists(m_name)) {
00331     Debug("log-file", "Roll not needed for %s; file doesn't exist", (m_name) ? m_name : "no_name");
00332     return 0;
00333   }
00334   // Read meta info if needed (if file was not opened)
00335   //
00336   if (!m_meta_info) {
00337     m_meta_info = new MetaInfo(m_name);
00338   }
00339   //
00340   // Create the new file name, which consists of a timestamp and rolled
00341   // extension added to the previous file name.  The timestamp format is
00342   // ".%Y%m%d.%Hh%Mm%Ss-%Y%m%d.%Hh%Mm%Ss", where the two date/time values
00343   // represent the starting and ending times for entries in the rolled
00344   // log file.  In addition, we add the hostname.  So, the entire rolled
00345   // format is something like:
00346   //
00347   //    "squid.log.mymachine.19980712.12h00m00s-19980713.12h00m00s.old"
00348   //
00349   char roll_name[MAXPATHLEN];
00350   char start_time_ext[64];
00351   char end_time_ext[64];
00352   time_t start, end;
00353 
00354   //
00355   // Make sure the file is closed so we don't leak any descriptors.
00356   //
00357   close_file();
00358 
00359   //
00360   // Start with conservative values for the start and end bounds, then
00361   // try to refine.
00362   //
00363   start = 0L;
00364   end = (interval_end >= m_end_time) ? interval_end : m_end_time;
00365 
00366   if (m_meta_info->data_from_metafile()) {
00367     //
00368     // If the metadata came from the metafile, this means that
00369     // the file was preexisting, so we can't use m_start_time for
00370     // our starting bounds.  Instead, we'll try to use the file
00371     // creation time stored in the metafile (if it's valid and we can
00372     // read it).  If all else fails, we'll use 0 for the start time.
00373     //
00374     m_meta_info->get_creation_time(&start);
00375   } else {
00376     //
00377     // The logfile was not preexisting (normal case), so we'll use
00378     // earlier of the interval start time and the m_start_time.
00379     //
00380     // note that m_start_time is not the time of the first
00381     // transaction, but the time of the creation of the first log
00382     // buffer used by the file. These times may be different,
00383     // especially under light load, and using the m_start_time may
00384     // produce overlapping filenames (the problem is that we have
00385     // no easy way of keeping track of the timestamp of the first
00386     // transaction
00387     //
00388     start = (m_start_time < interval_start) ? m_start_time : interval_start;
00389   }
00390 
00391   //
00392   // Now that we have our timestamp values, convert them to the proper
00393   // timestamp formats and create the rolled file name.
00394   //
00395   LogUtils::timestamp_to_str((long) start, start_time_ext, 64);
00396   LogUtils::timestamp_to_str((long) end, end_time_ext, 64);
00397   snprintf(roll_name, MAXPATHLEN, "%s%s%s.%s-%s%s",
00398                m_name,
00399                LOGFILE_SEPARATOR_STRING,
00400                Machine::instance()->hostname, start_time_ext, end_time_ext, LOGFILE_ROLLED_EXTENSION);
00401 
00402   //
00403   // It may be possible that the file we want to roll into already
00404   // exists.  If so, then we need to add a version tag to the rolled
00405   // filename as well so that we don't clobber existing files.
00406   //
00407 
00408   int version = 1;
00409   while (LogFile::exists(roll_name)) {
00410     Note("The rolled file %s already exists; adding version "
00411          "tag %d to avoid clobbering the existing file.", roll_name, version);
00412     snprintf(roll_name, MAXPATHLEN, "%s%s%s.%s-%s.%d%s",
00413                  m_name,
00414                  LOGFILE_SEPARATOR_STRING,
00415                  Machine::instance()->hostname, start_time_ext, end_time_ext, version, LOGFILE_ROLLED_EXTENSION);
00416     version++;
00417   }
00418 
00419   //
00420   // It's now safe to rename the file.
00421   //
00422 
00423   if (::rename(m_name, roll_name) < 0) {
00424     Warning("Traffic Server could not rename logfile %s to %s, error %d: "
00425             "%s.", m_name, roll_name, errno, strerror(errno));
00426     return 0;
00427   }
00428   // reset m_start_time
00429   //
00430   m_start_time = 0;
00431   m_bytes_written = 0;
00432 
00433   Debug("log-file", "The logfile %s was rolled to %s.", m_name, roll_name);
00434 
00435   return 1;
00436 }
00437 
00438 /*-------------------------------------------------------------------------
00439   LogFile::preproc_and_try_delete
00440 
00441   preprocess the given buffer data before write to target file
00442   and try to delete it when its reference become zero.
00443   -------------------------------------------------------------------------*/
00444 int
00445 LogFile::preproc_and_try_delete(LogBuffer * lb)
00446 {
00447   int ret = -1;
00448   LogBufferHeader *buffer_header;
00449 
00450   if (lb == NULL) {
00451     Note("Cannot write LogBuffer to LogFile %s; LogBuffer is NULL", m_name);
00452     return -1;
00453   }
00454 
00455   ink_atomic_increment(&lb->m_references, 1);
00456 
00457   if ((buffer_header = lb->header()) == NULL) {
00458     Note("Cannot write LogBuffer to LogFile %s; LogBufferHeader is NULL",
00459         m_name);
00460     goto done;
00461   }
00462   if (buffer_header->entry_count == 0) {
00463     // no bytes to write
00464     Note("LogBuffer with 0 entries for LogFile %s, nothing to write", m_name);
00465     goto done;
00466   }
00467 
00468   //
00469   // If the start time for this file has yet to be established, then grab
00470   // the low_timestamp from the given LogBuffer.  Then, we always set the
00471   // end time to the high_timestamp, so it's always up to date.
00472   //
00473   if (!m_start_time)
00474     m_start_time = buffer_header->low_timestamp;
00475   m_end_time = buffer_header->high_timestamp;
00476 
00477   if (m_file_format == LOG_FILE_BINARY) {
00478     //
00479     // Ok, now we need to write the binary buffer to the file, and we
00480     // can do so in one swift write.  The question is, do we write the
00481     // LogBufferHeader with each buffer or not?  The answer is yes.
00482     // Even though we'll be puttint down redundant data (things that
00483     // don't change between buffers), it's not worth trying to separate
00484     // out the buffer-dependent data from the buffer-independent data.
00485     //
00486     LogFlushData *flush_data = new LogFlushData(this, lb);
00487 
00488     ProxyMutex *mutex = this_thread()->mutex;
00489 
00490     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat,
00491                    lb->header()->entry_count);
00492 
00493     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat,
00494                    lb->header()->byte_count);
00495 
00496     ink_atomiclist_push(Log::flush_data_list, flush_data);
00497 
00498     Log::flush_notify->signal();
00499 
00500     //
00501     // LogBuffer will be deleted in flush thread
00502     //
00503     return 0;
00504   }
00505   else if (m_file_format == LOG_FILE_ASCII || m_file_format == LOG_FILE_PIPE) {
00506     write_ascii_logbuffer3(buffer_header);
00507     ret = 0;
00508   }
00509   else {
00510     Note("Cannot write LogBuffer to LogFile %s; invalid file format: %d",
00511          m_name, m_file_format);
00512   }
00513 
00514 done:
00515   LogBuffer::destroy(lb);
00516   return ret;
00517 }
00518 
00519 /*-------------------------------------------------------------------------
00520   LogFile::write_ascii_logbuffer
00521 
00522   This routine takes the given LogBuffer and writes it (in ASCII) to the
00523   given file descriptor.  Written as a stand-alone function, it can be
00524   called from either the local LogBuffer::write routine from inside of the
00525   proxy, or from an external program (since it is a static function).  The
00526   return value is the number of bytes written.
00527   -------------------------------------------------------------------------*/
00528 
00529 int
00530 LogFile::write_ascii_logbuffer(LogBufferHeader * buffer_header, int fd, const char *path, const char *alt_format)
00531 {
00532   ink_assert(buffer_header != NULL);
00533   ink_assert(fd >= 0);
00534 
00535   char fmt_buf[LOG_MAX_FORMATTED_BUFFER];
00536   char fmt_line[LOG_MAX_FORMATTED_LINE];
00537   LogBufferIterator iter(buffer_header);
00538   LogEntryHeader *entry_header;
00539   int fmt_buf_bytes = 0;
00540   int fmt_line_bytes = 0;
00541   int bytes = 0;
00542 
00543   LogFormatType format_type;
00544   char *fieldlist_str;
00545   char *printf_str;
00546 
00547   switch (buffer_header->version) {
00548   case LOG_SEGMENT_VERSION:
00549     format_type = (LogFormatType) buffer_header->format_type;
00550 
00551     fieldlist_str = buffer_header->fmt_fieldlist();
00552     printf_str = buffer_header->fmt_printf();
00553     break;
00554 
00555   default:
00556     Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
00557          "current version is %d", buffer_header->version, LOG_SEGMENT_VERSION);
00558     return 0;
00559   }
00560 
00561   while ((entry_header = iter.next())) {
00562     fmt_line_bytes = LogBuffer::to_ascii(entry_header, format_type,
00563                                          &fmt_line[0], LOG_MAX_FORMATTED_LINE,
00564                                          fieldlist_str, printf_str, buffer_header->version, alt_format);
00565     ink_assert(fmt_line_bytes > 0);
00566 
00567     if (fmt_line_bytes > 0) {
00568       if ((fmt_line_bytes + fmt_buf_bytes) >= LOG_MAX_FORMATTED_BUFFER) {
00569         if (!Log::config->logging_space_exhausted) {
00570           bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
00571         }
00572         fmt_buf_bytes = 0;
00573       }
00574       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
00575       ink_assert(fmt_line_bytes < LOG_MAX_FORMATTED_BUFFER - fmt_buf_bytes);
00576       memcpy(&fmt_buf[fmt_buf_bytes], fmt_line, fmt_line_bytes);
00577       fmt_buf_bytes += fmt_line_bytes;
00578       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
00579       fmt_buf[fmt_buf_bytes] = '\n';    // keep entries separate
00580       fmt_buf_bytes += 1;
00581     }
00582   }
00583   if (fmt_buf_bytes > 0) {
00584     if (!Log::config->logging_space_exhausted) {
00585       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
00586       bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
00587     }
00588   }
00589 
00590   return bytes;
00591 }
00592 
00593 int
00594 LogFile::write_ascii_logbuffer3(LogBufferHeader * buffer_header, const char *alt_format)
00595 {
00596   Debug("log-file", "entering LogFile::write_ascii_logbuffer3 for %s " "(this=%p)", m_name, this);
00597   ink_assert(buffer_header != NULL);
00598 
00599   ProxyMutex *mutex = this_thread()->mutex;
00600   LogBufferIterator iter(buffer_header);
00601   LogEntryHeader *entry_header;
00602   int fmt_entry_count = 0;
00603   int fmt_buf_bytes = 0;
00604   int total_bytes = 0;
00605 
00606   LogFormatType format_type;
00607   char *fieldlist_str;
00608   char *printf_str;
00609   char *ascii_buffer;
00610 
00611   switch (buffer_header->version) {
00612   case LOG_SEGMENT_VERSION:
00613     format_type = (LogFormatType) buffer_header->format_type;
00614     fieldlist_str = buffer_header->fmt_fieldlist();
00615     printf_str = buffer_header->fmt_printf();
00616     break;
00617 
00618   default:
00619     Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
00620          "current version is %d", buffer_header->version, LOG_SEGMENT_VERSION);
00621     return 0;
00622   }
00623 
00624   while ((entry_header = iter.next())) {
00625     fmt_entry_count = 0;
00626     fmt_buf_bytes = 0;
00627 
00628     if (m_file_format == LOG_FILE_PIPE)
00629       ascii_buffer = (char *)malloc(m_max_line_size);
00630     else
00631       ascii_buffer = (char *)malloc(m_ascii_buffer_size);
00632 
00633     // fill the buffer with as many records as possible
00634     //
00635     do {
00636       if (entry_header->entry_len >= m_max_line_size) {
00637         Warning("Log is too long(%" PRIu32 "), it would be truncated. max_len:%zu",
00638                 entry_header->entry_len, m_max_line_size);
00639       }
00640 
00641       int bytes = LogBuffer::to_ascii(entry_header, format_type,
00642                                       &ascii_buffer[fmt_buf_bytes],
00643                                       m_max_line_size - 1,
00644                                       fieldlist_str, printf_str,
00645                                       buffer_header->version,
00646                                       alt_format);
00647 
00648       if (bytes > 0) {
00649         fmt_buf_bytes += bytes;
00650         ascii_buffer[fmt_buf_bytes] = '\n';
00651         ++fmt_buf_bytes;
00652         ++fmt_entry_count;
00653       } else {
00654         Error("Failed to convert LogBuffer to ascii, have dropped (%" PRIu32 ") bytes.",
00655               entry_header->entry_len);
00656 
00657         RecIncrRawStat(log_rsb, mutex->thread_holding,
00658                        log_stat_num_lost_before_flush_to_disk_stat,
00659                        fmt_entry_count);
00660 
00661         RecIncrRawStat(log_rsb, mutex->thread_holding,
00662                        log_stat_bytes_lost_before_flush_to_disk_stat,
00663                        fmt_buf_bytes);
00664       }
00665       // if writing to a pipe, fill the buffer with a single
00666       // record to avoid as much as possible overflowing the
00667       // pipe buffer
00668       //
00669       if (m_file_format == LOG_FILE_PIPE)
00670         break;
00671 
00672       if (m_ascii_buffer_size - fmt_buf_bytes < m_max_line_size)
00673         break;
00674     } while ((entry_header = iter.next()));
00675 
00676     // send the buffer to flush thread
00677     //
00678     LogFlushData *flush_data = new LogFlushData(this, ascii_buffer, fmt_buf_bytes);
00679 
00680     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat,
00681                    fmt_entry_count);
00682 
00683     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat,
00684                    fmt_buf_bytes);
00685 
00686     ink_atomiclist_push(Log::flush_data_list, flush_data);
00687 
00688     Log::flush_notify->signal();
00689 
00690     total_bytes += fmt_buf_bytes;
00691   }
00692 
00693   return total_bytes;
00694 }
00695 
00696 /*-------------------------------------------------------------------------
00697   LogFile::writeln
00698 
00699   This function will make sure the following data is written to the
00700   output file (m_fd) with a trailing newline.
00701   -------------------------------------------------------------------------*/
00702 
00703 int
00704 LogFile::writeln(char *data, int len, int fd, const char *path)
00705 {
00706   int total_bytes = 0;
00707 
00708   if (len > 0 && data && fd >= 0) {
00709     struct iovec wvec[2];
00710     memset(&wvec, 0, sizeof(iovec));
00711     memset(&wvec[1], 0, sizeof(iovec));
00712     int bytes_this_write, vcnt = 1;
00713 
00714 #if defined(solaris)
00715     wvec[0].iov_base = (caddr_t) data;
00716 #else
00717     wvec[0].iov_base = (void *) data;
00718 #endif
00719     wvec[0].iov_len = (size_t) len;
00720 
00721     if (data[len - 1] != '\n') {
00722 #if defined(solaris)
00723       wvec[1].iov_base = (caddr_t) "\n";
00724 #else
00725       wvec[1].iov_base = (void *) "\n";
00726 #endif
00727       wvec[1].iov_len = (size_t) 1;
00728       vcnt++;
00729     }
00730 
00731     if ((bytes_this_write = (int)::writev(fd, (const struct iovec *) wvec, vcnt)) < 0) {
00732       Warning("An error was encountered in writing to %s: %s.", ((path) ? path : "logfile"), strerror(errno));
00733     } else
00734       total_bytes = bytes_this_write;
00735   }
00736   return total_bytes;
00737 }
00738 
00739 /*-------------------------------------------------------------------------
00740   LogFile::check_fd
00741 
00742   This routine will occasionally stat the current logfile to make sure that
00743   it really does exist.  The easiest way to do this is to close the file
00744   and re-open it, which will create the file if it doesn't already exist.
00745 
00746   Failure to open the logfile will generate a manager alarm and a Warning.
00747   -------------------------------------------------------------------------*/
00748 
00749 void
00750 LogFile::check_fd()
00751 {
00752   static bool failure_last_call = false;
00753   static unsigned stat_check_count = 1;
00754 
00755   if ((stat_check_count % Log::config->file_stat_frequency) == 0) {
00756     //
00757     // It's time to see if the file really exists.  If we can't see
00758     // the file (via access), then we'll close our descriptor and
00759     // attept to re-open it, which will create the file if it's not
00760     // there.
00761     //
00762     if (m_name && !LogFile::exists(m_name)) {
00763       close_file();
00764     }
00765     stat_check_count = 0;
00766   }
00767   stat_check_count++;
00768 
00769   int err = open_file();
00770   // XXX if open_file() returns, LOG_FILE_FILESYSTEM_CHECKS_FAILED, raise a more informative alarm ...
00771   if (err != LOG_FILE_NO_ERROR && err != LOG_FILE_NO_PIPE_READERS) {
00772     if (!failure_last_call) {
00773       LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, "Traffic Server could not open logfile %s.", m_name);
00774       Warning("Traffic Server could not open logfile %s: %s.", m_name, strerror(errno));
00775     }
00776     failure_last_call = true;
00777     return;
00778   }
00779 
00780   failure_last_call = false;
00781 }
00782 
00783 void
00784 LogFile::display(FILE * fd)
00785 {
00786   fprintf(fd, "Logfile: %s, %s\n", get_name(), (is_open())? "file is open" : "file is not open");
00787 }
00788 
00789 /***************************************************************************
00790  LogFileList IS NOT USED
00791 ****************************************************************************/
00792 
00793 
00794 /****************************************************************************
00795 
00796   MetaInfo methods
00797 
00798 *****************************************************************************/
00799 
00800 void
00801 MetaInfo::_build_name(const char *filename)
00802 {
00803   int i = -1, l = 0;
00804   char c;
00805   while (c = filename[l], c != 0) {
00806     if (c == '/') {
00807       i = l;
00808     }
00809     ++l;
00810   }
00811 
00812   // 7 = 1 (dot at beginning) + 5 (".meta") + 1 (null terminating)
00813   //
00814   _filename = (char *)ats_malloc(l + 7);
00815 
00816   if (i < 0) {
00817     ink_string_concatenate_strings(_filename, ".", filename, ".meta", NULL);
00818   } else {
00819     memcpy(_filename, filename, i + 1);
00820     ink_string_concatenate_strings(&_filename[i + 1], ".", &filename[i + 1]
00821                                    , ".meta", NULL);
00822   }
00823 }
00824 
00825 void
00826 MetaInfo::_read_from_file()
00827 {
00828   _flags |= DATA_FROM_METAFILE;
00829   int fd = open(_filename, O_RDONLY);
00830   if (fd < 0) {
00831     Warning("Could not open metafile %s for reading: %s", _filename, strerror(errno));
00832   } else {
00833     _flags |= FILE_OPEN_SUCCESSFUL;
00834     SimpleTokenizer tok('=', SimpleTokenizer::OVERWRITE_INPUT_STRING);
00835     int line_number = 1;
00836     while (ink_file_fd_readline(fd, BUF_SIZE, _buffer) > 0) {
00837       tok.setString(_buffer);
00838       char *t = tok.getNext();
00839       if (t) {
00840         if (strcmp(t, "creation_time") == 0) {
00841           t = tok.getNext();
00842           if (t) {
00843             _creation_time = (time_t) ink_atoi64(t);
00844             _flags |= VALID_CREATION_TIME;
00845           }
00846         } else if (strcmp(t, "object_signature") == 0) {
00847           t = tok.getNext();
00848           if (t) {
00849             _log_object_signature = ink_atoi64(t);
00850             _flags |= VALID_SIGNATURE;
00851             Debug("log-meta", "MetaInfo::_read_from_file\n"
00852                   "\tfilename = %s\n"
00853                   "\tsignature string = %s\n" "\tsignature value = %" PRIu64 "", _filename, t, _log_object_signature);
00854           }
00855         } else if (line_number == 1) {
00856           ink_release_assert(!"no panda support");
00857         }
00858       }
00859       ++line_number;
00860     }
00861     close(fd);
00862   }
00863 }
00864 
00865 void
00866 MetaInfo::_write_to_file()
00867 {
00868   int fd = open(_filename, O_WRONLY | O_CREAT | O_TRUNC,
00869                 Log::config->logfile_perm);
00870 
00871   if (fd <= 0) {
00872     Warning("Could not open metafile %s for writing: %s", _filename, strerror(errno));
00873   } else {
00874     int n;
00875     if (_flags & VALID_CREATION_TIME) {
00876       n = snprintf(_buffer, BUF_SIZE, "creation_time = %lu\n", (unsigned long) _creation_time);
00877       // TODO modify this runtime check so that it is not an assertion
00878       ink_release_assert(n <= BUF_SIZE);
00879       if (write(fd, _buffer, n) == -1) {
00880         Warning("Could not write creation_time");
00881       }
00882     }
00883     if (_flags & VALID_SIGNATURE) {
00884       n = snprintf(_buffer, BUF_SIZE, "object_signature = %" PRIu64 "\n", _log_object_signature);
00885       // TODO modify this runtime check so that it is not an assertion
00886       ink_release_assert(n <= BUF_SIZE);
00887       if (write(fd, _buffer, n) == -1) {
00888         Warning("Could not write object_signaure");
00889       }
00890       Debug("log-meta", "MetaInfo::_write_to_file\n"
00891             "\tfilename = %s\n"
00892             "\tsignature value = %" PRIu64 "\n" "\tsignature string = %s", _filename, _log_object_signature, _buffer);
00893     }
00894   }
00895   close(fd);
00896 }

Generated by  doxygen 1.7.1