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

LogConfig.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   This file implements the LogConfig object.
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 #include "libts.h"
00025 #include "I_Layout.h"
00026 
00027 #ifdef HAVE_SYS_PARAM_H
00028 #include <sys/param.h>
00029 #endif
00030 
00031 #include "ink_platform.h"
00032 #include "ink_file.h"
00033 
00034 #include "Main.h"
00035 #include "List.h"
00036 #include "InkXml.h"
00037 
00038 #include "Log.h"
00039 #include "LogField.h"
00040 #include "LogFilter.h"
00041 #include "LogFormat.h"
00042 #include "LogFile.h"
00043 #include "LogBuffer.h"
00044 #include "LogHost.h"
00045 #include "LogObject.h"
00046 #include "LogConfig.h"
00047 #include "LogUtils.h"
00048 #include "SimpleTokenizer.h"
00049 
00050 #include "LogCollationAccept.h"
00051 #include "LogPredefined.h"
00052 
00053 #define DISK_IS_CONFIG_FULL_MESSAGE \
00054     "Access logging to local log directory suspended - " \
00055     "configured space allocation exhausted."
00056 #define DISK_IS_ACTUAL_FULL_MESSAGE \
00057     "Access logging to local log directory suspended - " \
00058     "no more space on the logging partition."
00059 #define DISK_IS_CONFIG_LOW_MESSAGE \
00060     "Access logging to local log directory suspended - " \
00061     "configured space allocation almost exhausted."
00062 #define DISK_IS_ACTUAL_LOW_MESSAGE \
00063     "Access logging to local log directory suspended - partition space is low."
00064 #define DUP_FORMAT_MESSAGE \
00065     "Format named %s already exists; duplicate format names are not allowed."
00066 
00067 #define PARTITION_HEADROOM_MB   10
00068 
00069 void
00070 LogConfig::setup_default_values()
00071 {
00072   const unsigned int bufSize = 512;
00073   char name[bufSize];
00074   if (!gethostname(name, bufSize)) {
00075     ink_strlcpy(name, "unknown_host_name", sizeof(name));
00076   }
00077   hostname = ats_strdup(name);
00078 
00079   log_buffer_size = (int) (10 * LOG_KILOBYTE);
00080   max_secs_per_buffer = 5;
00081   max_space_mb_for_logs = 100;
00082   max_space_mb_for_orphan_logs = 25;
00083   max_space_mb_headroom = 10;
00084   logfile_perm = 0644;
00085   logfile_dir = ats_strdup(".");
00086 
00087   separate_icp_logs = 1;
00088   separate_host_logs = false;
00089 
00090   squid_log_enabled = true;
00091   squid_log_is_ascii = true;
00092   squid_log_name = ats_strdup("squid");
00093   squid_log_header = NULL;
00094 
00095   common_log_enabled = false;
00096   common_log_is_ascii = true;
00097   common_log_name = ats_strdup("common");
00098   common_log_header = NULL;
00099 
00100   extended_log_enabled = false;
00101   extended_log_is_ascii = true;
00102   extended_log_name = ats_strdup("extended");
00103   extended_log_header = NULL;
00104 
00105   extended2_log_enabled = false;
00106   extended2_log_is_ascii = true;
00107   extended2_log_name = ats_strdup("extended2");
00108   extended2_log_header = NULL;
00109 
00110   collation_mode = Log::NO_COLLATION;
00111   collation_host = ats_strdup("none");
00112   collation_port = 0;
00113   collation_host_tagged = false;
00114   collation_preproc_threads = 1;
00115   collation_secret = ats_strdup("foobar");
00116   collation_retry_sec = 0;
00117   collation_max_send_buffers = 0;
00118 
00119   rolling_enabled = Log::NO_ROLLING;
00120   rolling_interval_sec = 86400; // 24 hours
00121   rolling_offset_hr = 0;
00122   rolling_size_mb = 10;
00123   auto_delete_rolled_files = true;
00124   roll_log_files_now = false;
00125 
00126   custom_logs_enabled = false;
00127   xml_config_file = ats_strdup("logs_xml.config");
00128   hosts_config_file = ats_strdup("log_hosts.config");
00129 
00130 /* The default values for the search log                         */
00131 
00132   search_log_enabled = false;
00133 /* Comma separated filter names. These Filetrs are defined in    */
00134 /* log_xml.config file. One such filter defines to reject URLs   */
00135 /* with .gif extension. These filters are added to search log    */
00136 /* object.                                                       */
00137   search_log_filters = NULL;
00138   search_rolling_interval_sec = 86400;  // 24 hours
00139   search_server_ip_addr = 0;
00140   search_server_port = 8080;
00141   search_top_sites = 100;
00142 /* Comma separated filter names. These Filetrs are defined in    */
00143 /* records.config or thru UI. These are applied to the search log*/
00144 /* while parsing to generate top 'n' site summary file. The URLs */
00145 /* containing these strings will not be parsed.                  */
00146   search_url_filter = NULL;
00147 /* Logging system captures all the URLs in this log file.        */
00148   search_log_file_one = ats_strdup("search_log1");
00149 /* Logging system captures only cache miss URLs to this log file.*/
00150   search_log_file_two = ats_strdup("search_log2");
00151 
00152   sampling_frequency = 1;
00153   file_stat_frequency = 16;
00154   space_used_frequency = 900;
00155 
00156   use_orphan_log_space_value = false;
00157 
00158   ascii_buffer_size = 4 * 9216;
00159   max_line_size = 9216;         // size of pipe buffer for SunOS 5.6
00160 }
00161 
00162 void *
00163 LogConfig::reconfigure_mgmt_variables(void * /* token ATS_UNUSED */, char * /* data_raw ATS_UNUSED */,
00164                                       int /* data_len ATS_UNUSED */)
00165 {
00166   Note("[TrafficServer:LogConfig] : Roll_Log_Files event received. rolling now");
00167   Log::config->roll_log_files_now = true;
00168   return NULL;
00169 }
00170 
00171 void
00172 LogConfig::read_configuration_variables()
00173 {
00174   int val;
00175   char *ptr;
00176 
00177   val = (int) REC_ConfigReadInteger("proxy.config.log.log_buffer_size");
00178   if (val > 0) {
00179     log_buffer_size = val;
00180   }
00181 
00182   val = (int) REC_ConfigReadInteger("proxy.config.log.max_secs_per_buffer");
00183   if (val > 0) {
00184     max_secs_per_buffer = val;
00185   }
00186 
00187   val = (int) REC_ConfigReadInteger("proxy.config.log.max_space_mb_for_logs");
00188   if (val > 0) {
00189     max_space_mb_for_logs = val;
00190   }
00191 
00192   val = (int) REC_ConfigReadInteger("proxy.config.log.max_space_mb_for_orphan_logs");
00193   if (val > 0) {
00194     max_space_mb_for_orphan_logs = val;
00195   }
00196 
00197   val = (int) REC_ConfigReadInteger("proxy.config.log.max_space_mb_headroom");
00198   if (val > 0) {
00199     max_space_mb_headroom = val;
00200   }
00201 
00202   // TODO: We should mover this "parser" to lib/ts
00203   ptr = REC_ConfigReadString("proxy.config.log.logfile_perm");
00204   if (ptr && strlen(ptr) == 9) {
00205     logfile_perm = 0;
00206     char *c = ptr;
00207     if (*c == 'r')
00208       logfile_perm |= S_IRUSR;
00209     c++;
00210     if (*c == 'w')
00211       logfile_perm |= S_IWUSR;
00212     c++;
00213     if (*c == 'x')
00214       logfile_perm |= S_IXUSR;
00215     c++;
00216     if (*c == 'r')
00217       logfile_perm |= S_IRGRP;
00218     c++;
00219     if (*c == 'w')
00220       logfile_perm |= S_IWGRP;
00221     c++;
00222     if (*c == 'x')
00223       logfile_perm |= S_IXGRP;
00224     c++;
00225     if (*c == 'r')
00226       logfile_perm |= S_IROTH;
00227     c++;
00228     if (*c == 'w')
00229       logfile_perm |= S_IWOTH;
00230     c++;
00231     if (*c == 'x')
00232       logfile_perm |= S_IXOTH;
00233     ats_free(ptr);
00234   }
00235 
00236   ptr = REC_ConfigReadString("proxy.config.log.hostname");
00237   if (ptr != NULL) {
00238     ats_free(hostname);
00239     hostname = ptr;
00240   }
00241 
00242   ats_free(logfile_dir);
00243   logfile_dir = RecConfigReadLogDir();
00244 
00245   if (access(logfile_dir, R_OK | W_OK | X_OK) == -1) {
00246     // Try 'system_root_dir/var/log/trafficserver' directory
00247     fprintf(stderr,"unable to access log directory '%s': %d, %s\n",
00248             logfile_dir, errno, strerror(errno));
00249     fprintf(stderr,"please set 'proxy.config.log.logfile_dir'\n");
00250     _exit(1);
00251   }
00252 
00253   //
00254   // for each predefined logging format, we need to know:
00255   //   - whether logging in that format is enabled
00256   //   - if we're logging to a file, what the name and format (ASCII,
00257   //     BINARY) are
00258   //   - what header should be written down at the start of each file for
00259   //     this format
00260   // this is accomplished with four config variables per format:
00261   //   "proxy.config.log.<format>_log_enabled" INT
00262   //   "proxy.config.log.<format>_log_is_ascii" INT
00263   //   "proxy.config.log.<format>_log_name" STRING
00264   //   "proxy.config.log.<format>_log_header" STRING
00265   //
00266 
00267 
00268   // SQUID
00269   val = (int) REC_ConfigReadInteger("proxy.config.log.squid_log_enabled");
00270   squid_log_enabled = (val > 0);
00271 
00272   val = (int) REC_ConfigReadInteger("proxy.config.log.squid_log_is_ascii");
00273   squid_log_is_ascii = (val > 0);
00274 
00275   ptr = REC_ConfigReadString("proxy.config.log.squid_log_name");
00276   if (ptr != NULL) {
00277     ats_free(squid_log_name);
00278     squid_log_name = ptr;
00279   }
00280 
00281   ptr = REC_ConfigReadString("proxy.config.log.squid_log_header");
00282   if (ptr != NULL) {
00283     ats_free(squid_log_header);
00284     squid_log_header = ptr;
00285   }
00286 
00287   // COMMON
00288   val = (int) REC_ConfigReadInteger("proxy.config.log.common_log_enabled");
00289   common_log_enabled = (val > 0);
00290 
00291   val = (int) REC_ConfigReadInteger("proxy.config.log.common_log_is_ascii");
00292   common_log_is_ascii = (val > 0);
00293 
00294   ptr = REC_ConfigReadString("proxy.config.log.common_log_name");
00295   if (ptr != NULL) {
00296     ats_free(common_log_name);
00297     common_log_name = ptr;
00298   }
00299 
00300   ptr = REC_ConfigReadString("proxy.config.log.common_log_header");
00301   if (ptr != NULL) {
00302     ats_free(common_log_header);
00303     common_log_header = ptr;
00304   }
00305 
00306   // EXTENDED
00307   val = (int) REC_ConfigReadInteger("proxy.config.log.extended_log_enabled");
00308   extended_log_enabled = (val > 0);
00309 
00310   val = (int) REC_ConfigReadInteger("proxy.config.log.extended_log_is_ascii");
00311   extended_log_is_ascii = (val > 0);
00312 
00313   ptr = REC_ConfigReadString("proxy.config.log.extended_log_name");
00314   if (ptr != NULL) {
00315     ats_free(extended_log_name);
00316     extended_log_name = ptr;
00317   }
00318 
00319   ptr = REC_ConfigReadString("proxy.config.log.extended_log_header");
00320   if (ptr != NULL) {
00321     ats_free(extended_log_header);
00322     extended_log_header = ptr;
00323   }
00324 
00325   // EXTENDED2
00326   val = (int) REC_ConfigReadInteger("proxy.config.log.extended2_log_enabled");
00327   extended2_log_enabled = (val > 0);
00328 
00329   val = (int) REC_ConfigReadInteger("proxy.config.log.extended2_log_is_ascii");
00330   extended2_log_is_ascii = (val > 0);
00331 
00332   ptr = REC_ConfigReadString("proxy.config.log.extended2_log_name");
00333   if (ptr != NULL) {
00334     ats_free(extended2_log_name);
00335     extended2_log_name = ptr;
00336   }
00337 
00338   ptr = REC_ConfigReadString("proxy.config.log.extended2_log_header");
00339   if (ptr != NULL) {
00340     ats_free(extended2_log_header);
00341     extended2_log_header = ptr;
00342   }
00343 
00344 
00345   // SPLITTING
00346   // 0 means no splitting
00347   // 1 means splitting
00348   // for icp
00349   //   -1 means filter out (do not log and do not create split file)
00350   val = (int) REC_ConfigReadInteger("proxy.config.log.separate_icp_logs");
00351   separate_icp_logs = (val > 0);
00352 
00353   val = (int) REC_ConfigReadInteger("proxy.config.log.separate_host_logs");
00354   separate_host_logs = (val > 0);
00355 
00356 
00357   // COLLATION
00358   val = (int) REC_ConfigReadInteger("proxy.local.log.collation_mode");
00359   // do not restrict value so that error message is logged if
00360   // collation_mode is out of range
00361   collation_mode = val;
00362 
00363   ptr = REC_ConfigReadString("proxy.config.log.collation_host");
00364   if (ptr != NULL) {
00365     ats_free(collation_host);
00366     collation_host = ptr;
00367   }
00368 
00369   val = (int) REC_ConfigReadInteger("proxy.config.log.collation_port");
00370   if (val >= 0) {
00371     collation_port = val;
00372   }
00373 
00374   val = (int) REC_ConfigReadInteger("proxy.config.log.collation_host_tagged");
00375   collation_host_tagged = (val > 0);
00376 
00377   val = (int) REC_ConfigReadInteger("proxy.config.log.collation_preproc_threads");
00378   if (val > 0 && val <= 128) {
00379     collation_preproc_threads = val;
00380   }
00381 
00382   ptr = REC_ConfigReadString("proxy.config.log.collation_secret");
00383   if (ptr != NULL) {
00384     ats_free(collation_secret);
00385     collation_secret = ptr;
00386   }
00387 
00388   val = (int) REC_ConfigReadInteger("proxy.config.log.collation_retry_sec");
00389   if (val >= 0) {
00390     collation_retry_sec = val;
00391   }
00392 
00393   val = (int) REC_ConfigReadInteger("proxy.config.log.collation_max_send_buffers");
00394   if (val >= 0) {
00395     collation_max_send_buffers = val;
00396   }
00397 
00398 
00399   // ROLLING
00400 
00401   // we don't check for valid values of rolling_enabled, rolling_interval_sec,
00402   // rolling_offset_hr, or rolling_size_mb because the LogObject takes care of this
00403   //
00404   rolling_interval_sec = (int) REC_ConfigReadInteger("proxy.config.log.rolling_interval_sec");
00405   rolling_offset_hr = (int) REC_ConfigReadInteger("proxy.config.log.rolling_offset_hr");
00406   rolling_size_mb = (int) REC_ConfigReadInteger("proxy.config.log.rolling_size_mb");
00407 
00408   val = (int) REC_ConfigReadInteger("proxy.config.log.rolling_enabled");
00409   if (LogRollingEnabledIsValid(val)) {
00410     rolling_enabled = (Log::RollingEnabledValues)val;
00411   } else {
00412     Warning("invalid value '%d' for '%s', disabling log rolling", val, "proxy.config.log.rolling_enabled");
00413     rolling_enabled = Log::NO_ROLLING;
00414   }
00415 
00416   val = (int) REC_ConfigReadInteger("proxy.config.log.auto_delete_rolled_files");
00417   auto_delete_rolled_files = (val > 0);
00418 
00419   // CUSTOM LOGGING
00420   val = (int) REC_ConfigReadInteger("proxy.config.log.custom_logs_enabled");
00421   custom_logs_enabled = (val > 0);
00422 
00423   ptr = REC_ConfigReadString("proxy.config.log.xml_config_file");
00424   if (ptr != NULL) {
00425     ats_free(xml_config_file);
00426     xml_config_file = ptr;
00427   }
00428 
00429   ptr = REC_ConfigReadString("proxy.config.log.hosts_config_file");
00430   if (ptr != NULL) {
00431     ats_free(hosts_config_file);
00432     hosts_config_file = ptr;
00433   }
00434 
00435   // PERFORMANCE
00436   val = (int) REC_ConfigReadInteger("proxy.config.log.sampling_frequency");
00437   if (val > 0) {
00438     sampling_frequency = val;
00439   }
00440 
00441   val = (int) REC_ConfigReadInteger("proxy.config.log.file_stat_frequency");
00442   if (val > 0) {
00443     file_stat_frequency = val;
00444   }
00445 
00446   val = (int) REC_ConfigReadInteger("proxy.config.log.space_used_frequency");
00447   if (val > 0) {
00448     space_used_frequency = val;
00449   }
00450 
00451   // ASCII BUFFER
00452   val = (int) REC_ConfigReadInteger("proxy.config.log.ascii_buffer_size");
00453   if (val > 0) {
00454     ascii_buffer_size = val;
00455   }
00456 
00457   val = (int) REC_ConfigReadInteger("proxy.config.log.max_line_size");
00458   if (val > 0) {
00459     max_line_size = val;
00460   }
00461 
00462 /* The following variables are initialized after reading the     */
00463 /* variable values from records.config                           */
00464 
00465   val = (int) REC_ConfigReadInteger("proxy.config.log.search_log_enabled");
00466   if (Log::logging_mode == Log::LOG_MODE_FULL)
00467     search_log_enabled = (val > 0);
00468 
00469 /*                                                               */
00470 /* The following collects the filter names to be added to search */
00471 /* log object. User can define the filter to exclude URLs with   */
00472 /* certain file extensions from being logged.                    */
00473 /*                                                               */
00474   ptr = REC_ConfigReadString("proxy.config.log.search_log_filters");
00475   if (ptr != NULL) {
00476     search_log_filters = ptr;
00477   }
00478 
00479 /*                                                               */
00480 /* This filter information is used while parsing the search log  */
00481 /* to generate top 'n' site sorted URL summary file.             */
00482 /* This log file contains top 'n' number of frequently accessed  */
00483 /* sites. This file is sent to search server mentioned by the    */
00484 /* pair ip-address & port.                                       */
00485 /*                                                               */
00486   ptr = REC_ConfigReadString("proxy.config.log.search_url_filter");
00487   if (ptr != NULL) {
00488     search_url_filter = ptr;
00489   }
00490 
00491   val = (int) REC_ConfigReadInteger("proxy.config.log.search_top_sites");
00492   if (val > 0) {
00493     search_top_sites = val;
00494   }
00495 
00496   ptr = REC_ConfigReadString("proxy.config.log.search_server_ip_addr");
00497   if (ptr != NULL) {
00498     unsigned int ipaddr;
00499     ipaddr = inet_addr(ptr);
00500     if (ipaddr > 0) {
00501       search_server_ip_addr = ipaddr;
00502     } else
00503       search_server_ip_addr = 0;
00504   }
00505 
00506   val = (int) REC_ConfigReadInteger("proxy.config.log.search_server_port");
00507   if (val > 0) {
00508     search_server_port = val;
00509   }
00510 
00511 /*  Rolling interval is taken care in LogObject.                 */
00512   val = (int) REC_ConfigReadInteger("proxy.config.log.search_rolling_interval_sec");
00513   if (val > 0) {
00514     search_rolling_interval_sec = val;
00515   }
00516 }
00517 
00518 /*-------------------------------------------------------------------------
00519   LogConfig::LogConfig
00520 
00521   Read the logging configuration variables from the config file and
00522   initialize the LogConfig member variables.  Assign some meaningful
00523   default value if we get garbage back from the config file.
00524   -------------------------------------------------------------------------*/
00525 
00526 // TODO: Is UINT_MAX here really correct?
00527 LogConfig::LogConfig()
00528   : initialized(false),
00529     reconfiguration_needed(false),
00530     logging_space_exhausted(false), m_space_used(0), m_partition_space_left((int64_t) UINT_MAX),
00531     m_log_collation_accept(NULL),
00532     m_dir_entry(NULL),
00533     m_pDir(NULL),
00534     m_disk_full(false),
00535     m_disk_low(false), m_partition_full(false), m_partition_low(false), m_log_directory_inaccessible(false)
00536 {
00537   // Setup the default values for all LogConfig public variables so that
00538   // a LogConfig object is valid upon return from the constructor even
00539   // if no configuration file is read
00540   //
00541   setup_default_values();
00542 }
00543 
00544 /*-------------------------------------------------------------------------
00545   LogConfig::~LogConfig
00546 
00547   Delete all config variable strings.
00548   -------------------------------------------------------------------------*/
00549 
00550 LogConfig::~LogConfig()
00551 {
00552 
00553 // we don't delete the log collation accept because it may be transferred
00554 // to another LogConfig object
00555 //
00556 //    delete m_log_collation_accept;
00557 
00558   ats_free(hostname);
00559   ats_free(logfile_dir);
00560   ats_free(squid_log_name);
00561   ats_free(squid_log_header);
00562   ats_free(common_log_name);
00563   ats_free(common_log_header);
00564   ats_free(extended_log_name);
00565   ats_free(extended_log_header);
00566   ats_free(extended2_log_name);
00567   ats_free(extended2_log_header);
00568   ats_free(collation_host);
00569   ats_free(collation_secret);
00570   ats_free(xml_config_file);
00571   ats_free(hosts_config_file);
00572   ats_free(search_log_file_one);
00573   ats_free(search_log_file_two);
00574   ats_free(m_dir_entry);
00575 }
00576 
00577 
00578 /*-------------------------------------------------------------------------
00579   LogConfig::setup_collation
00580   -------------------------------------------------------------------------*/
00581 
00582 void
00583 LogConfig::setup_collation(LogConfig * prev_config)
00584 {
00585   // Set-up the collation status, but only if collation is enabled and
00586   // there are valid entries for the collation host and port.
00587   //
00588   if (collation_mode < Log::NO_COLLATION || collation_mode >= Log::N_COLLATION_MODES) {
00589     Note("Invalid value %d for proxy.local.log.collation_mode"
00590          " configuration variable (valid range is from %d to %d)\n"
00591          "Log collation disabled", collation_mode, Log::NO_COLLATION, Log::N_COLLATION_MODES - 1);
00592   } else if (collation_mode == Log::NO_COLLATION) {
00593     // if the previous configuration had a collation accept, delete it
00594     //
00595     if (prev_config && prev_config->m_log_collation_accept) {
00596       delete prev_config->m_log_collation_accept;
00597       prev_config->m_log_collation_accept = NULL;
00598     }
00599   } else {
00600     if (!collation_port) {
00601       Note("Cannot activate log collation, %d is an invalid collation port", collation_port);
00602     } else if (collation_mode > Log::COLLATION_HOST && strcmp(collation_host, "none") == 0) {
00603       Note("Cannot activate log collation, \"%s\" is an invalid collation host", collation_host);
00604     } else {
00605       if (collation_mode == Log::COLLATION_HOST) {
00606 
00607         ink_assert(m_log_collation_accept == 0);
00608 
00609         if (prev_config && prev_config->m_log_collation_accept) {
00610           if (prev_config->collation_port == collation_port) {
00611             m_log_collation_accept = prev_config->m_log_collation_accept;
00612           } else {
00613             delete prev_config->m_log_collation_accept;
00614           }
00615         }
00616 
00617         if (!m_log_collation_accept) {
00618           Log::collation_port = collation_port;
00619           m_log_collation_accept = new LogCollationAccept(collation_port);
00620         }
00621         Debug("log", "I am a collation host listening on port %d.", collation_port);
00622       } else {
00623         Debug("log", "I am a collation client (%d)."
00624               " My collation host is %s:%d", collation_mode, collation_host, collation_port);
00625       }
00626 
00627       Debug("log", "using iocore log collation");
00628       if (collation_host_tagged) {
00629         LogFormat::turn_tagging_on();
00630       } else {
00631         LogFormat::turn_tagging_off();
00632       }
00633     }
00634   }
00635 }
00636 
00637 /*-------------------------------------------------------------------------
00638   LogConfig::init
00639   -------------------------------------------------------------------------*/
00640 
00641 void
00642 LogConfig::init(LogConfig * prev_config)
00643 {
00644   LogObject * errlog = NULL;
00645 
00646   ink_assert(!initialized);
00647 
00648   setup_collation(prev_config);
00649 
00650   update_space_used();
00651 
00652   // create log objects
00653   //
00654   if (Log::transaction_logging_enabled()) {
00655     setup_log_objects();
00656   }
00657 
00658   // ----------------------------------------------------------------------
00659   // Construct a new error log object candidate.
00660   if (Log::error_logging_enabled()) {
00661     PreDefinedFormatInfo * info;
00662 
00663     Debug("log", "creating predefined error log object");
00664     info = MakePredefinedErrorLog(this);
00665     errlog = this->create_predefined_object(info, 0, NULL);
00666     errlog->set_fmt_timestamps();
00667     delete info;
00668   } else {
00669     Log::error_log = NULL;
00670   }
00671 
00672   if (prev_config) {
00673     // Transfer objects from previous configuration.
00674     transfer_objects(prev_config);
00675 
00676     // After transferring objects, we are going to keep either the new error log or the old one. Figure out
00677     // which one we are keeping and make that the global ...
00678     if (Log::error_log) {
00679       errlog = this->log_object_manager.find_by_format_name(Log::error_log->m_format->name());
00680     }
00681   }
00682 
00683   ink_atomic_swap(&Log::error_log, errlog);
00684 
00685   // determine if we should use the orphan log space value or not
00686   // we use it if all objects are collation clients, or if some are and
00687   // the specified space for collation is larger than that for local files
00688   //
00689   size_t num_collation_clients = log_object_manager.get_num_collation_clients();
00690   use_orphan_log_space_value =
00691     (num_collation_clients == 0 ? false :
00692      (log_object_manager.get_num_objects() == num_collation_clients ? true :
00693       max_space_mb_for_orphan_logs > max_space_mb_for_logs));
00694 
00695   initialized = true;
00696 }
00697 
00698 /*-------------------------------------------------------------------------
00699   LogConfig::display
00700 
00701   Dump the values for the current LogConfig object.
00702   -------------------------------------------------------------------------*/
00703 
00704 void
00705 LogConfig::display(FILE * fd)
00706 {
00707   fprintf(fd, "-----------------------------\n");
00708   fprintf(fd, "--- Logging Configuration ---\n");
00709   fprintf(fd, "-----------------------------\n");
00710   fprintf(fd, "Config variables:\n");
00711   fprintf(fd, "   log_buffer_size = %d\n", log_buffer_size);
00712   fprintf(fd, "   max_secs_per_buffer = %d\n", max_secs_per_buffer);
00713   fprintf(fd, "   max_space_mb_for_logs = %d\n", max_space_mb_for_logs);
00714   fprintf(fd, "   max_space_mb_for_orphan_logs = %d\n", max_space_mb_for_orphan_logs);
00715   fprintf(fd, "   use_orphan_log_space_value = %d\n", use_orphan_log_space_value);
00716   fprintf(fd, "   max_space_mb_headroom = %d\n", max_space_mb_headroom);
00717   fprintf(fd, "   hostname = %s\n", hostname);
00718   fprintf(fd, "   logfile_dir = %s\n", logfile_dir);
00719   fprintf(fd, "   logfile_perm = 0%o\n", logfile_perm);
00720   fprintf(fd, "   xml_config_file = %s\n", xml_config_file);
00721   fprintf(fd, "   hosts_config_file = %s\n", hosts_config_file);
00722   fprintf(fd, "   squid_log_enabled = %d\n", squid_log_enabled);
00723   fprintf(fd, "   squid_log_is_ascii = %d\n", squid_log_is_ascii);
00724   fprintf(fd, "   squid_log_name = %s\n", squid_log_name);
00725   fprintf(fd, "   squid_log_header = %s\n", squid_log_header ? squid_log_header : "<no header defined>");
00726   fprintf(fd, "   common_log_enabled = %d\n", common_log_enabled);
00727   fprintf(fd, "   common_log_is_ascii = %d\n", common_log_is_ascii);
00728   fprintf(fd, "   common_log_name = %s\n", common_log_name);
00729   fprintf(fd, "   common_log_header = %s\n", common_log_header ? common_log_header : "<no header defined>");
00730   fprintf(fd, "   extended_log_enabled = %d\n", extended_log_enabled);
00731   fprintf(fd, "   extended_log_is_ascii = %d\n", extended_log_is_ascii);
00732   fprintf(fd, "   extended_log_name = %s\n", extended_log_name);
00733   fprintf(fd, "   extended_log_header = %s\n", extended_log_header ? extended_log_header : "<no header defined>");
00734   fprintf(fd, "   extended2_log_enabled = %d\n", extended2_log_enabled);
00735   fprintf(fd, "   extended2_log_is_ascii = %d\n", extended2_log_is_ascii);
00736   fprintf(fd, "   extended2_log_name = %s\n", extended2_log_name);
00737   fprintf(fd, "   extended2_log_header = %s\n", extended2_log_header ? extended2_log_header : "<no header defined>");
00738   fprintf(fd, "   separate_icp_logs = %d\n", separate_icp_logs);
00739   fprintf(fd, "   separate_host_logs = %d\n", separate_host_logs);
00740   fprintf(fd, "   collation_mode = %d\n", collation_mode);
00741   fprintf(fd, "   collation_host = %s\n", collation_host);
00742   fprintf(fd, "   collation_port = %d\n", collation_port);
00743   fprintf(fd, "   collation_host_tagged = %d\n", collation_host_tagged);
00744   fprintf(fd, "   collation_preproc_threads = %d\n", collation_preproc_threads);
00745   fprintf(fd, "   collation_secret = %s\n", collation_secret);
00746   fprintf(fd, "   rolling_enabled = %d\n", rolling_enabled);
00747   fprintf(fd, "   rolling_interval_sec = %d\n", rolling_interval_sec);
00748   fprintf(fd, "   rolling_offset_hr = %d\n", rolling_offset_hr);
00749   fprintf(fd, "   rolling_size_mb = %d\n", rolling_size_mb);
00750   fprintf(fd, "   auto_delete_rolled_files = %d\n", auto_delete_rolled_files);
00751   fprintf(fd, "   sampling_frequency = %d\n", sampling_frequency);
00752   fprintf(fd, "   file_stat_frequency = %d\n", file_stat_frequency);
00753   fprintf(fd, "   space_used_frequency = %d\n", space_used_frequency);
00754 
00755   fprintf(fd, "\n");
00756   fprintf(fd, "************ Log Objects (%u objects) ************\n", (unsigned int)log_object_manager.get_num_objects());
00757   log_object_manager.display(fd);
00758 
00759   fprintf(fd, "************ Global Filter List (%u filters) ************\n", global_filter_list.count());
00760   global_filter_list.display(fd);
00761 
00762   fprintf(fd, "************ Global Format List (%u formats) ************\n", global_format_list.count());
00763   global_format_list.display(fd);
00764 }
00765 
00766 /*                                                               */
00767 /* The user defined filters are added to the search_one          */
00768 /* log object. These filters are defined to filter the images    */
00769 /* and vedio file etc., URLs. These filters depends on the       */
00770 /* end-user's search requirements.                               */
00771 /*                                                               */
00772 void
00773 LogConfig::add_filters_to_search_log_object(const char *format_name)
00774 {
00775   LogObject *obj;
00776 
00777   obj = log_object_manager.find_by_format_name(format_name);
00778 
00779   /* Apply the user defined filters on to newly created LogObject */
00780   SimpleTokenizer tok(search_log_filters, ',');
00781   char *filter_name;
00782   while (filter_name = tok.getNext(), filter_name != 0) {
00783     LogFilter *f;
00784     f = global_filter_list.find_by_name(filter_name);
00785     if (!f) {
00786       Warning("Filter %s not in the global filter list; cannot add to this LogObject", filter_name);
00787     } else {
00788       obj->add_filter(f);
00789     }
00790   }
00791 
00792 }
00793 
00794 //-----------------------------------------------------------------------------
00795 // Create one object for each of the entries in the pre-defined info list
00796 // and apply the specified filter(s) to it.
00797 //
00798 // Normally, only one pre-defined format has been selected on the config file
00799 // so this function creates a single object in such case.
00800 //
00801 // This function adds the pre-defined objects to the global_object_list.
00802 //
00803 
00804 LogObject *
00805 LogConfig::create_predefined_object(const PreDefinedFormatInfo * pdi, size_t num_filters,
00806                                                   LogFilter ** filter, const char *filt_name, bool force_extension)
00807 {
00808   const char *obj_fname;
00809   char obj_filt_fname[PATH_NAME_MAX];
00810 
00811   ink_release_assert(pdi != NULL);
00812 
00813   if (filt_name) {
00814     ink_string_concatenate_strings_n(obj_filt_fname, PATH_NAME_MAX, pdi->filename, "-", filt_name, NULL);
00815     obj_fname = obj_filt_fname;
00816   } else {
00817     obj_fname = pdi->filename;
00818   }
00819 
00820   if (force_extension) {
00821     switch (pdi->filefmt) {
00822       case LOG_FILE_ASCII:
00823         ink_string_append(obj_filt_fname, (char *)LOG_FILE_ASCII_OBJECT_FILENAME_EXTENSION, PATH_NAME_MAX);
00824         break;
00825       case LOG_FILE_BINARY:
00826         ink_string_append(obj_filt_fname, (char *)LOG_FILE_BINARY_OBJECT_FILENAME_EXTENSION, PATH_NAME_MAX);
00827         break;
00828       default:
00829         break;
00830     }
00831   }
00832 
00833   // create object with filters
00834   //
00835   LogObject *obj;
00836   obj = new LogObject(pdi->format, logfile_dir, obj_fname,
00837                       pdi->filefmt, pdi->header, (Log::RollingEnabledValues)rolling_enabled,
00838                       collation_preproc_threads, rolling_interval_sec,
00839                       rolling_offset_hr, rolling_size_mb);
00840 
00841   if (pdi->collatable) {
00842     if (collation_mode == Log::SEND_STD_FMTS || collation_mode == Log::SEND_STD_AND_NON_XML_CUSTOM_FMTS) {
00843 
00844       LogHost *loghost = new LogHost(obj->get_full_filename(), obj->get_signature());
00845       ink_assert(loghost != NULL);
00846 
00847       loghost->set_name_port(collation_host, collation_port);
00848       obj->add_loghost(loghost, false);
00849     }
00850   }
00851 
00852   for (size_t i = 0; i < num_filters; ++i) {
00853     obj->add_filter(filter[i]);
00854   }
00855 
00856   // give object to object manager
00857   if (log_object_manager.manage_object(obj) != LogObjectManager::NO_FILENAME_CONFLICTS) {
00858     delete obj;
00859     return NULL;
00860   }
00861 
00862   return obj;
00863 }
00864 
00865 void
00866 LogConfig::create_predefined_objects_with_filter(const PreDefinedFormatList & predef, size_t nfilters,
00867                                                   LogFilter ** filters, const char * filt_name, bool force_extension)
00868 {
00869   PreDefinedFormatInfo *pdi;
00870 
00871   for (pdi = predef.formats.head; pdi != NULL; pdi = (pdi->link).next) {
00872     this->create_predefined_object(pdi, nfilters, filters, filt_name, force_extension);
00873   }
00874 }
00875 
00876 
00877 //-----------------------------------------------------------------------------
00878 // split_by_protocol
00879 //
00880 // This function creates the objects needed to log different protocols on
00881 // their own file if any of the "separate_xxx_logs" config. variable is set.
00882 //
00883 // Upon return, the pf_list argument holds the filters that reject the
00884 // protocols for which objects have been created. This filter list is used
00885 // to create the rest of the pre defined log objects.
00886 //
00887 // As input, this function requires a list wilh all information regarding
00888 // pre-defined formats.
00889 //
00890 LogFilter *
00891 LogConfig::split_by_protocol(const PreDefinedFormatList & predef)
00892 {
00893   if (!separate_icp_logs) {
00894     return NULL;
00895   }
00896   // http MUST be last entry
00897   enum { icp=0,
00898          http
00899   };
00900 
00901   int64_t value[] = { LOG_ENTRY_ICP,
00902                     LOG_ENTRY_HTTP
00903   };
00904   const char *name[] = { "icp", "http" };
00905   const char *filter_name[] = { "__icp__", "__http__" };
00906   int64_t filter_val[http];    // protocols to reject
00907   size_t n = 0;
00908 
00909   LogField *etype_field = Log::global_field_list.find_by_symbol("etype");
00910   ink_assert(etype_field);
00911 
00912   if (separate_icp_logs) {
00913     if (separate_icp_logs == 1) {
00914       LogFilter * filter[1];
00915 
00916       filter[0] = new LogFilterInt(filter_name[icp], etype_field, LogFilter::ACCEPT, LogFilter::MATCH, value[icp]);
00917       create_predefined_objects_with_filter(predef, countof(filter), filter, name[icp]);
00918       delete filter[0];
00919     }
00920     filter_val[n++] = value[icp];
00921   }
00922 
00923   // At this point, separate objects for all protocols except http
00924   // have been created if requested. We now add to the argument list
00925   // the filters needed to reject the protocols that have already
00926   // been taken care of. Note that we do not test for http since
00927   // there is no "separate_http_logs" config variable and thus http
00928   // could not have been taken care of at this point
00929   //
00930 
00931   if (n > 0) {
00932     return new LogFilterInt("__reject_protocols__", etype_field, LogFilter::REJECT, LogFilter::MATCH, n, filter_val);
00933   }
00934   return NULL;
00935 }
00936 
00937 size_t
00938 LogConfig::split_by_hostname(const PreDefinedFormatList & predef, LogFilter * reject_protocol_filter)
00939 {
00940   size_t n_hosts;
00941   char **host = read_log_hosts_file(&n_hosts);  // allocates memory for array
00942 
00943   if (n_hosts) {
00944 
00945     size_t num_filt = 0;
00946     LogFilter *rp_ah[2];        // rejected protocols + accepted host
00947     LogField *shn_field = Log::global_field_list.find_by_symbol("shn");
00948     ink_assert(shn_field);
00949 
00950     if (reject_protocol_filter) {
00951       rp_ah[num_filt++] = reject_protocol_filter;
00952     }
00953 
00954     for (size_t i = 0; i < n_hosts; ++i) {
00955 
00956       // add a filter that accepts the specified hostname
00957       //
00958       char filter_name[LOG_MAX_FORMAT_LINE + 12];
00959       snprintf(filter_name, LOG_MAX_FORMAT_LINE, "__accept_%s__", host[i]);
00960       rp_ah[num_filt] = new LogFilterString(filter_name, shn_field, LogFilter::ACCEPT,
00961                                             LogFilter::CASE_INSENSITIVE_CONTAIN, host[i]);
00962 
00963       create_predefined_objects_with_filter(predef, num_filt + 1, rp_ah, host[i], true);
00964       delete rp_ah[num_filt];
00965     }
00966 
00967     LogFilter *rp_rh[2];        // rejected protocols + rejected hosts
00968 
00969     num_filt = 0;
00970 
00971     if (reject_protocol_filter) {
00972       rp_rh[num_filt++] = reject_protocol_filter;
00973     }
00974 
00975     rp_rh[num_filt] = new LogFilterString("__reject_hosts__", shn_field, LogFilter::REJECT,
00976                                           LogFilter::CASE_INSENSITIVE_CONTAIN, n_hosts, host);
00977 
00978     // create the "catch-all" object that contains logs for all
00979     // hosts other than those specified in the hosts file and for
00980     // those protocols that do not have their own file
00981     //
00982     create_predefined_objects_with_filter(predef, num_filt + 1, rp_rh);
00983     delete rp_rh[num_filt];
00984 
00985     delete[]host;               // deallocate memory allocated by
00986     // read_log_hosts_file
00987   }
00988   // host is not allocated unless n_hosts > 0
00989   // coverity[leaked_storage]
00990   return n_hosts;
00991 }
00992 
00993 //-----------------------------------------------------------------------------
00994 // setup_log_objects
00995 //
00996 // Construct:
00997 //
00998 // -All objects necessary to log the pre defined formats considering
00999 //  protocol and host splitting.
01000 // -All custom objects.
01001 //
01002 // Upon return from this function:
01003 // - global_object_list has the aforementioned objects
01004 // - global_format_list has all the pre-determined log formats (e.g. squid,
01005 //   common, etc.)
01006 // - global_filter_list has all custom filters
01007 //   Note that the filters necessary to do log splitting for the pre defined
01008 //   format are kept private (e.g., they do not go to the global_filter_list).
01009 //
01010 void
01011 LogConfig::setup_log_objects()
01012 {
01013   Debug("log", "creating objects...");
01014 
01015   // ----------------------------------------------------------------------
01016   // Construct the LogObjects for the pre-defined formats.
01017 
01018   // gather the config information for the pre-defined formats
01019   //
01020   PreDefinedFormatList predef;
01021 
01022   predef.init(this);
01023 
01024   // do protocol splitting
01025   //
01026   LogFilter *reject_protocol_filter = split_by_protocol(predef);
01027 
01028   // do host splitting
01029   //
01030   size_t num_hosts = 0;
01031   if (separate_host_logs) {
01032     num_hosts = split_by_hostname(predef, reject_protocol_filter);
01033   }
01034 
01035   if (num_hosts == 0) {
01036     // if no host splitting was requested, or if host splitting
01037     // was not successful (e.g. empty log_hosts.config file) then
01038     // create the "catch-all" object that contains logs for all
01039     // protocols that do not have their own file, and for all hosts
01040     //
01041     LogFilter *f[1];
01042     f[0] = reject_protocol_filter;
01043     create_predefined_objects_with_filter(predef, countof(f), f);
01044   }
01045 
01046   delete reject_protocol_filter;
01047 
01048   // ----------------------------------------------------------------------
01049   // Construct the LogObjects for the custom formats
01050 
01051   global_filter_list.clear();
01052 
01053   /*                                                               */
01054   /* create search Log Objects here.                               */
01055   /* The following creates the search_one, search_two log objects  */
01056   /* For search_one log object user defines filters in xml config  */
01057   /* file. The names of these filters have to be given in          */
01058   /* records.config file                                           */
01059   /*                                                               */
01060   if (search_log_enabled) {
01061     Debug("log", "creating search log object");
01062     /* Read xml configuration for search log from memory.            */
01063     read_xml_log_config(1);
01064   }
01065 
01066   if (custom_logs_enabled) {
01067     /* Read xml configuration from logs_xml.config file.             */
01068     read_xml_log_config(0);
01069   }
01070 
01071   /*                                                               */
01072   /* Add the user defined filters to search_one log object.        */
01073   /*                                                               */
01074   if (search_log_enabled) {
01075     if (search_log_filters) {
01076       add_filters_to_search_log_object("m_search_one");
01077       add_filters_to_search_log_object("m_search_two");
01078     }
01079   }
01080   // open local pipes so readers can see them
01081   //
01082   log_object_manager.open_local_pipes();
01083 
01084   if (is_debug_tag_set("log")) {
01085     log_object_manager.display();
01086   }
01087 }
01088 
01089 /*-------------------------------------------------------------------------
01090   LogConfig::reconfigure
01091 
01092   This is the manager callback for any logging config variable change.
01093   Since we want to access the config variables to build a new config
01094   object, but can't from this function (big lock technology in the
01095   manager), we'll just set a flag and call the real reconfiguration
01096   function from the logging thread.
01097   -------------------------------------------------------------------------*/
01098 
01099 int
01100 LogConfig::reconfigure(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */,
01101                        RecData /* data ATS_UNUSED */, void * /* cookie ATS_UNUSED */)
01102 {
01103   Debug("log-config", "Reconfiguration request accepted");
01104   Log::config->reconfiguration_needed = true;
01105   return 0;
01106 }
01107 
01108 /*-------------------------------------------------------------------------
01109   LogConfig::register_config_callbacks
01110 
01111   This static function is called by Log::init to register the config update
01112   function for each of the logging configuration variables.
01113   -------------------------------------------------------------------------*/
01114 
01115 void
01116 LogConfig::register_config_callbacks()
01117 {
01118   static const char * names[] = {
01119     "proxy.config.log.log_buffer_size",
01120     "proxy.config.log.max_secs_per_buffer",
01121     "proxy.config.log.max_space_mb_for_logs",
01122     "proxy.config.log.max_space_mb_for_orphan_logs",
01123     "proxy.config.log.max_space_mb_headroom",
01124     "proxy.config.log.logfile_perm",
01125     "proxy.config.log.hostname",
01126     "proxy.config.log.logfile_dir",
01127     "proxy.config.log.squid_log_enabled",
01128     "proxy.config.log.squid_log_is_ascii",
01129     "proxy.config.log.squid_log_name",
01130     "proxy.config.log.squid_log_header",
01131     "proxy.config.log.common_log_enabled",
01132     "proxy.config.log.common_log_is_ascii",
01133     "proxy.config.log.common_log_name",
01134     "proxy.config.log.common_log_header",
01135     "proxy.config.log.extended_log_enabled",
01136     "proxy.config.log.extended_log_is_ascii",
01137     "proxy.config.log.extended_log_name",
01138     "proxy.config.log.extended_log_header",
01139     "proxy.config.log.extended2_log_enabled",
01140     "proxy.config.log.extended2_log_is_ascii",
01141     "proxy.config.log.extended2_log_name",
01142     "proxy.config.log.extended2_log_header",
01143     "proxy.config.log.separate_icp_logs",
01144     "proxy.config.log.separate_host_logs",
01145     "proxy.local.log.collation_mode",
01146     "proxy.config.log.collation_host",
01147     "proxy.config.log.collation_port",
01148     "proxy.config.log.collation_host_tagged",
01149     "proxy.config.log.collation_secret",
01150     "proxy.config.log.collation_retry_sec",
01151     "proxy.config.log.collation_max_send_buffers",
01152     "proxy.config.log.rolling_enabled",
01153     "proxy.config.log.rolling_interval_sec",
01154     "proxy.config.log.rolling_offset_hr",
01155     "proxy.config.log.rolling_size_mb",
01156     "proxy.config.log.auto_delete_rolled_files",
01157     "proxy.config.log.custom_logs_enabled",
01158     "proxy.config.log.xml_config_file",
01159     "proxy.config.log.hosts_config_file",
01160     "proxy.config.log.sampling_frequency",
01161     "proxy.config.log.file_stat_frequency",
01162     "proxy.config.log.space_used_frequency",
01163     "proxy.config.log.search_rolling_interval_sec",
01164     "proxy.config.log.search_log_enabled",
01165     "proxy.config.log.search_top_sites",
01166     "proxy.config.log.search_server_ip_addr",
01167     "proxy.config.log.search_server_port",
01168     "proxy.config.log.search_url_filter",
01169   };
01170 
01171 
01172   for (unsigned i = 0; i < countof(names); ++i) {
01173     REC_RegisterConfigUpdateFunc(names[i], &LogConfig::reconfigure, NULL);
01174   }
01175 }
01176 
01177 /*-------------------------------------------------------------------------
01178   LogConfig::register_stat_callbacks
01179 
01180   This static function is called by Log::init to register the stat update
01181   function for each of the logging stats variables.
01182   -------------------------------------------------------------------------*/
01183 
01184 void
01185 LogConfig::register_stat_callbacks()
01186 {
01187   //
01188   // events
01189   //
01190   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01191                      "proxy.process.log.event_log_error_ok",
01192                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_error_ok_stat, RecRawStatSyncCount);
01193   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01194                      "proxy.process.log.event_log_error_skip",
01195                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_error_skip_stat, RecRawStatSyncCount);
01196   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01197                      "proxy.process.log.event_log_error_aggr",
01198                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_error_aggr_stat, RecRawStatSyncCount);
01199   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01200                      "proxy.process.log.event_log_error_full",
01201                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_error_full_stat, RecRawStatSyncCount);
01202   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01203                      "proxy.process.log.event_log_error_fail",
01204                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_error_fail_stat, RecRawStatSyncCount);
01205   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01206                      "proxy.process.log.event_log_access_ok",
01207                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_access_ok_stat, RecRawStatSyncCount);
01208   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01209                      "proxy.process.log.event_log_access_skip",
01210                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_access_skip_stat, RecRawStatSyncCount);
01211   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01212                      "proxy.process.log.event_log_access_aggr",
01213                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_access_aggr_stat, RecRawStatSyncCount);
01214   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01215                      "proxy.process.log.event_log_access_full",
01216                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_access_full_stat, RecRawStatSyncCount);
01217   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01218                      "proxy.process.log.event_log_access_fail",
01219                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_event_log_access_fail_stat, RecRawStatSyncCount);
01220   //
01221   // number vs bytes of logs
01222   //
01223   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01224                      "proxy.process.log.num_sent_to_network",
01225                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_num_sent_to_network_stat, RecRawStatSyncSum);
01226   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01227                      "proxy.process.log.num_lost_before_sent_to_network",
01228                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_num_lost_before_sent_to_network_stat, RecRawStatSyncSum);
01229   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01230                      "proxy.process.log.num_received_from_network",
01231                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_num_received_from_network_stat, RecRawStatSyncSum);
01232   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01233                      "proxy.process.log.num_flush_to_disk",
01234                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_num_flush_to_disk_stat, RecRawStatSyncSum);
01235   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01236                      "proxy.process.log.num_lost_before_flush_to_disk",
01237                      RECD_COUNTER, RECP_PERSISTENT, (int) log_stat_num_lost_before_flush_to_disk_stat, RecRawStatSyncSum);
01238   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01239                      "proxy.process.log.bytes_lost_before_preproc",
01240                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_lost_before_preproc_stat, RecRawStatSyncSum);
01241   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01242                      "proxy.process.log.bytes_sent_to_network",
01243                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_sent_to_network_stat, RecRawStatSyncSum);
01244   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01245                      "proxy.process.log.bytes_lost_before_sent_to_network",
01246                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_lost_before_sent_to_network_stat, RecRawStatSyncSum);
01247   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01248                      "proxy.process.log.bytes_received_from_network",
01249                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_received_from_network_stat, RecRawStatSyncSum);
01250   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01251                      "proxy.process.log.bytes_flush_to_disk",
01252                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_flush_to_disk_stat, RecRawStatSyncSum);
01253   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01254                      "proxy.process.log.bytes_lost_before_flush_to_disk",
01255                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_lost_before_flush_to_disk_stat, RecRawStatSyncSum);
01256   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01257                      "proxy.process.log.bytes_written_to_disk",
01258                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_written_to_disk_stat, RecRawStatSyncSum);
01259   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01260                      "proxy.process.log.bytes_lost_before_written_to_disk",
01261                      RECD_INT, RECP_PERSISTENT, (int) log_stat_bytes_lost_before_written_to_disk_stat, RecRawStatSyncSum);
01262   //
01263   // I/O
01264   //
01265   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01266                      "proxy.process.log.log_files_open",
01267                      RECD_COUNTER, RECP_NON_PERSISTENT, (int) log_stat_log_files_open_stat, RecRawStatSyncSum);
01268   RecRegisterRawStat(log_rsb, RECT_PROCESS,
01269                      "proxy.process.log.log_files_space_used",
01270                      RECD_INT, RECP_NON_PERSISTENT, (int) log_stat_log_files_space_used_stat, RecRawStatSyncSum);
01271 }
01272 
01273 /*-------------------------------------------------------------------------
01274   LogConfig::register_mgmt_callbacks
01275 
01276   This static function is called by Log::init to register the mgmt callback
01277   function for each of the logging mgmt messages.
01278   -------------------------------------------------------------------------*/
01279 
01280 void
01281 LogConfig::register_mgmt_callbacks()
01282 {
01283   RecRegisterManagerCb(REC_EVENT_ROLL_LOG_FILES, &LogConfig::reconfigure_mgmt_variables, NULL);
01284 }
01285 
01286 
01287 /*-------------------------------------------------------------------------
01288   LogConfig::space_to_write
01289 
01290   This function returns true if there is enough disk space to write the
01291   given number of bytes, false otherwise.
01292   -------------------------------------------------------------------------*/
01293 
01294 bool LogConfig::space_to_write(int64_t bytes_to_write)
01295 {
01296   int64_t config_space, partition_headroom;
01297   int64_t logical_space_used, physical_space_left;
01298   bool space;
01299 
01300   config_space = (int64_t) get_max_space_mb() * LOG_MEGABYTE;
01301   partition_headroom = (int64_t) PARTITION_HEADROOM_MB * LOG_MEGABYTE;
01302 
01303   logical_space_used = m_space_used + bytes_to_write;
01304   physical_space_left = m_partition_space_left - (int64_t) bytes_to_write;
01305 
01306   space = ((logical_space_used<config_space) && (physical_space_left> partition_headroom));
01307 
01308   Debug("logspace", "logical space used %" PRId64 ", configured space %" PRId64
01309       ", physical space left %" PRId64 ", partition headroom %" PRId64 ", space %s available",
01310       logical_space_used, config_space, physical_space_left, partition_headroom,
01311       space ? "is" : "is not");
01312 
01313   return space;
01314 }
01315 
01316 /*-------------------------------------------------------------------------
01317   LogConfig::update_space_used
01318 
01319   Update the m_space_used variable by reading the logging dir and counting
01320   the total bytes being occupied by files.  If we've used too much space
01321   (space_used > max_space - headroom) then start deleting some files (if
01322   auto_delete_rolled_files is set) to make room. Finally, update the
01323   space_used stat.
01324 
01325   This routine will only be executed SINGLE-THREADED, either by the main
01326   thread when a LogConfig is initialized, or by the event thread during the
01327   periodic space check.
01328   -------------------------------------------------------------------------*/
01329 
01330 static int
01331 delete_candidate_compare(const LogDeleteCandidate * a, const LogDeleteCandidate * b)
01332 {
01333   return ((int) (a->mtime - b->mtime));
01334 }
01335 
01336 void
01337 LogConfig::update_space_used()
01338 {
01339   // no need to update space used if log directory is inaccessible
01340   //
01341   if (m_log_directory_inaccessible)
01342     return;
01343 
01344   static const int MAX_CANDIDATES = 128;
01345   LogDeleteCandidate candidates[MAX_CANDIDATES];
01346   int i, victim, candidate_count;
01347   int64_t total_space_used, partition_space_left;
01348   char path[MAXPATHLEN];
01349   int sret;
01350   struct dirent *result;
01351   struct stat sbuf;
01352   DIR *ld;
01353 
01354   // check if logging directory has been specified
01355   //
01356   if (!logfile_dir) {
01357     const char *msg = "Logging directory not specified";
01358     Error("%s", msg);
01359     LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, "%s", msg);
01360     m_log_directory_inaccessible = true;
01361     return;
01362   }
01363   // check if logging directory exists and is searchable readable & writable
01364   //
01365   int err;
01366   do {
01367     err = access(logfile_dir, R_OK | W_OK | X_OK);
01368   } while ((err < 0) && (errno == EINTR));
01369 
01370   if (err < 0) {
01371     const char *msg = "Error accessing logging directory %s: %s.";
01372     Error(msg, logfile_dir, strerror(errno));
01373     LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, logfile_dir, strerror(errno));
01374     m_log_directory_inaccessible = true;
01375     return;
01376   }
01377 
01378   ld =::opendir(logfile_dir);
01379   if (ld == NULL) {
01380     const char *msg = "Error opening logging directory %s to perform a space check: %s.";
01381     Error(msg, logfile_dir, strerror(errno));
01382     LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, msg, logfile_dir, strerror(errno));
01383     m_log_directory_inaccessible = true;
01384     return;
01385   }
01386 
01387   if (!m_dir_entry) {
01388     long name_max = pathconf(logfile_dir, _PC_NAME_MAX);
01389 
01390     // pathconf should not fail after access and opendir have succeeded
01391     //
01392     ink_release_assert(name_max > 0);
01393 
01394     m_dir_entry = (struct dirent *)ats_malloc(sizeof(struct dirent) + name_max + 1);
01395   }
01396 
01397   total_space_used = 0LL;
01398 
01399   candidate_count = 0;
01400 
01401   while (readdir_r(ld, m_dir_entry, &result) == 0) {
01402 
01403     if (!result) {
01404       break;
01405     }
01406 
01407     snprintf(path, MAXPATHLEN, "%s/%s", logfile_dir, m_dir_entry->d_name);
01408 
01409     sret =::stat(path, &sbuf);
01410     if (sret != -1 && S_ISREG(sbuf.st_mode)) {
01411 
01412       total_space_used += (int64_t) sbuf.st_size;
01413 
01414       if (auto_delete_rolled_files && LogFile::rolled_logfile(m_dir_entry->d_name) && candidate_count < MAX_CANDIDATES) {
01415         //
01416         // then add this entry to the candidate list
01417         //
01418         candidates[candidate_count].name = ats_strdup(path);
01419         candidates[candidate_count].size = (int64_t) sbuf.st_size;
01420         candidates[candidate_count].mtime = sbuf.st_mtime;
01421         candidate_count++;
01422       }
01423     }
01424   }
01425   ::closedir(ld);
01426 
01427   //
01428   // Now check the partition to see if there is enough *actual* space.
01429   //
01430   partition_space_left = m_partition_space_left;
01431 
01432   struct statvfs fs;
01433 
01434   if (::statvfs(logfile_dir, &fs) >= 0) {
01435     partition_space_left = (int64_t) fs.f_bavail * (int64_t) fs.f_bsize;
01436   }
01437 
01438   //
01439   // Update the config variables for space used/left
01440   //
01441   m_space_used = total_space_used;
01442   m_partition_space_left = partition_space_left;
01443   RecSetRawStatSum(log_rsb, log_stat_log_files_space_used_stat, m_space_used);
01444   RecSetRawStatCount(log_rsb, log_stat_log_files_space_used_stat, 1);
01445 
01446   Debug("logspace", "%" PRId64 " bytes being used for logs", m_space_used);
01447   Debug("logspace", "%" PRId64 " bytes left on partition", m_partition_space_left);
01448 
01449   //
01450   // Now that we have an accurate picture of the amount of space being
01451   // used by logging, we can see if we're running low on space.  If so,
01452   // we might consider deleting some files that are stored in the
01453   // candidate array.
01454   //
01455   // To delete oldest files first, we'll sort our candidate array by
01456   // timestamps, making the oldest files first in the array (thus first
01457   // selected).
01458   //
01459 
01460   int64_t max_space = (int64_t) get_max_space_mb() * LOG_MEGABYTE;
01461   int64_t headroom = (int64_t) max_space_mb_headroom * LOG_MEGABYTE;
01462 
01463   if (candidate_count > 0 && !space_to_write(headroom)) {
01464 
01465     Debug("logspace", "headroom reached, trying to clear space ...");
01466     Debug("logspace", "sorting %d delete candidates ...", candidate_count);
01467     qsort(candidates, candidate_count, sizeof(LogDeleteCandidate),
01468           (int (*)(const void *, const void *)) delete_candidate_compare);
01469 
01470     for (victim = 0; victim < candidate_count; victim++) {
01471 
01472       if (space_to_write(headroom + log_buffer_size)) {
01473         Debug("logspace", "low water mark reached; stop deleting");
01474         break;
01475       }
01476 
01477       Debug("logspace", "auto-deleting %s", candidates[victim].name);
01478 
01479       if (unlink(candidates[victim].name) < 0) {
01480         Note("Traffic Server was Unable to auto-delete rolled "
01481              "logfile %s: %s.", candidates[victim].name, strerror(errno));
01482       } else {
01483         Debug("logspace", "The rolled logfile, %s, was auto-deleted; "
01484                "%" PRId64 " bytes were reclaimed.", candidates[victim].name, candidates[victim].size);
01485         m_space_used -= candidates[victim].size;
01486         m_partition_space_left += candidates[victim].size;
01487       }
01488     }
01489   }
01490   //
01491   // Clean up the candidate array
01492   //
01493   for (i = 0; i < candidate_count; i++) {
01494     ats_free(candidates[i].name);
01495   }
01496 
01497   //
01498   // Now that we've updated the m_space_used value, see if we need to
01499   // issue any alarms or warnings about space
01500   //
01501 
01502 
01503   if (!space_to_write(headroom)) {
01504     if (!logging_space_exhausted)
01505       Note("Logging space exhausted, any logs writing to local disk will be dropped!");
01506 
01507     logging_space_exhausted = true;
01508     //
01509     // Despite our best efforts, we still can't write to the disk.
01510     // Find out why and set/clear warnings.
01511     //
01512     // First, are we out of space based on configuration?
01513     //
01514     if (m_space_used >= max_space) {
01515       if (!m_disk_full) {
01516         m_disk_full = true;
01517         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, DISK_IS_CONFIG_FULL_MESSAGE);
01518         Warning(DISK_IS_CONFIG_FULL_MESSAGE);
01519       }
01520     }
01521     //
01522     // How about out of actual space on the partition?
01523     //
01524     else if (m_partition_space_left <= 0) {
01525       if (!m_partition_full) {
01526         m_partition_full = true;
01527         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, DISK_IS_ACTUAL_FULL_MESSAGE);
01528         Warning(DISK_IS_ACTUAL_FULL_MESSAGE);
01529       }
01530     }
01531     //
01532     // How about being within the headroom limit?
01533     //
01534     else if (m_space_used + headroom >= max_space) {
01535       if (!m_disk_low) {
01536         m_disk_low = true;
01537         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, DISK_IS_CONFIG_LOW_MESSAGE);
01538         Warning(DISK_IS_CONFIG_LOW_MESSAGE);
01539       }
01540     } else {
01541       if (!m_partition_low) {
01542         m_partition_low = true;
01543         LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, DISK_IS_ACTUAL_LOW_MESSAGE);
01544         Warning(DISK_IS_ACTUAL_LOW_MESSAGE);
01545       }
01546     }
01547   } else {
01548     //
01549     // We have enough space to log again; clear any previous messages
01550     //
01551     if (logging_space_exhausted)
01552       Note("Logging space is no longer exhausted.");
01553 
01554     logging_space_exhausted = false;
01555     if (m_disk_full || m_partition_full) {
01556       Note("Logging disk is no longer full; access logging to local log directory resumed.");
01557       m_disk_full = false;
01558       m_partition_full = false;
01559     }
01560     if (m_disk_low || m_partition_low) {
01561       Note("Logging disk is no longer low; access logging to local log directory resumed.");
01562       m_disk_low = false;
01563       m_partition_low = false;
01564     }
01565   }
01566 }
01567 
01568 
01569 /*-------------------------------------------------------------------------
01570   LogConfig::read_xml_log_config
01571 
01572   This is a new routine for reading the XML-based log config file.
01573   -------------------------------------------------------------------------*/
01574 
01575 static char xml_config_buffer[] = "<LogFilter> \
01576                                   <Name = \"reject_gif\"/> \
01577                                   <Action = \"REJECT\"/> \
01578                                   <Condition = \"cqup CASE_INSENSITIVE_CONTAIN .gif\"/> \
01579                                   </LogFilter>\
01580                                               \
01581                                   <LogFilter> \
01582                                   <Name = \"reject_jpg\"/> \
01583                                   <Action = \"REJECT\"/> \
01584                                   <Condition = \"cqup CASE_INSENSITIVE_CONTAIN .jpg\"/> \
01585                                   </LogFilter>\
01586                                               \
01587                                   <LogFilter> \
01588                                   <Name      = \"only_cache_miss\"/> \
01589                                   <Action    = \"ACCEPT\"/> \
01590                                   <Condition = \"crc MATCH TCP_MISS\"/> \
01591                                   </LogFilter> \
01592                                               \
01593                                   <LogFormat> \
01594                                   <Name = \"m_search_one\"/> \
01595                                   <Format = \"%%<cquc> %%<cqtt>\"/> \
01596                                   </LogFormat> \
01597                                                \
01598                                   <LogFormat> \
01599                                   <Name = \"m_search_two\"/> \
01600                                   <Format = \"%%<cquc> %%<crc>\"/> \
01601                                   </LogFormat> \
01602                                                \
01603                                   <LogObject> \
01604                                   <Filename = \"search_log1\"/> \
01605                                   <Format = \"m_search_one\"/> \
01606                                   <Filters = \"reject_gif\", \"reject_jpg\"/> \
01607                                   <RollingIntervalSec = \"%d\"/> \
01608                                   </LogObject> \
01609                                                \
01610                                   <LogObject> \
01611                                   <Filename = \"search_log2\"/> \
01612                                   <Format = \"m_search_two\"/> \
01613                                   <Filters = \"reject_gif\", \"reject_jpg\", \"only_cache_miss\"/> \
01614                                   <RollingIntervalSec = \"%d\"/> \
01615                                   </LogObject>";
01616 void
01617 LogConfig::read_xml_log_config(int from_memory)
01618 {
01619   ats_scoped_str config_path;
01620 
01621   if (!from_memory) {
01622     if (xml_config_file == NULL) {
01623       Note("No log config file to read");
01624       return;
01625     }
01626 
01627     config_path = Layout::get()->relative_to(Layout::get()->sysconfdir, xml_config_file);
01628   }
01629 
01630   InkXmlConfigFile log_config(config_path ? (const char *)config_path : "memory://builtin");
01631 
01632   if (!from_memory) {
01633     Debug("log-config", "Reading log config file %s", (const char *)config_path);
01634     Debug("xml", "%s is an XML-based config file", (const char *)config_path);
01635 
01636     if (log_config.parse() < 0) {
01637       Note("Error parsing log config file %s; ensure that it is XML-based", (const char *)config_path);
01638       return;
01639     }
01640 
01641     if (is_debug_tag_set("xml")) {
01642       log_config.display();
01643     }
01644   } else {
01645     int filedes[2];
01646     int nbytes = sizeof(xml_config_buffer);
01647     const size_t ptr_size = nbytes + 20;
01648     char *ptr = (char *)ats_malloc(ptr_size);
01649 
01650     if (pipe(filedes) != 0) {
01651       Note("xml parsing: Error in Opening a pipe");
01652       ats_free(ptr);
01653       return;
01654     }
01655 
01656     snprintf(ptr, ptr_size, xml_config_buffer, search_rolling_interval_sec, search_rolling_interval_sec);
01657     nbytes = strlen(ptr);
01658     if (write(filedes[1], ptr, nbytes) != nbytes) {
01659       Note("Error in writing to pipe.");
01660       ats_free(ptr);
01661       close(filedes[1]);
01662       close(filedes[0]);
01663       return;
01664     }
01665     ats_free(ptr);
01666     close(filedes[1]);
01667 
01668     if (log_config.parse(filedes[0]) < 0) {
01669       Note("Error parsing log config info from memory.");
01670       return;
01671     }
01672   }
01673 
01674 
01675   //
01676   // At this point, the XMl file has been parsed into a list of
01677   // InkXmlObjects.  We'll loop through them and add the information to
01678   // our current configuration.  Expected object names include:
01679   //
01680   //     LogFormat
01681   //     LogFilter
01682   //     LogObject
01683   //
01684 
01685   InkXmlObject *xobj;
01686   InkXmlAttr *xattr;
01687 
01688   for (xobj = log_config.first(); xobj; xobj = log_config.next(xobj)) {
01689 
01690     Debug("xml", "XmlObject: %s", xobj->object_name());
01691 
01692     if (strcmp(xobj->object_name(), "LogFormat") == 0) {
01693 
01694       //
01695       // Manditory attributes: Name, Format
01696       // Optional attributes : Interval (for aggregate operators)
01697       //
01698       NameList name;
01699       NameList format;
01700       NameList interval;
01701 
01702       for (xattr = xobj->first(); xattr; xattr = xobj->next(xattr)) {
01703         Debug("xml", "XmlAttr  : <%s,%s>", xattr->tag(), xattr->value());
01704 
01705         if (strcasecmp(xattr->tag(), "Name") == 0) {
01706           name.enqueue(xattr->value());
01707         } else if (strcasecmp(xattr->tag(), "Format") == 0) {
01708           format.enqueue(xattr->value());
01709         } else if (strcasecmp(xattr->tag(), "Interval") == 0) {
01710           interval.enqueue(xattr->value());
01711         } else {
01712           Note("Unknown attribute %s for %s; ignoring", xattr->tag(), xobj->object_name());
01713         }
01714       }
01715 
01716       // check integrity constraints
01717       //
01718       if (name.count() == 0) {
01719         Note("'Name' attribute missing for LogFormat object");
01720         continue;
01721       }
01722       if (format.count() == 0) {
01723         Note("'Format' attribute missing for LogFormat object");
01724         continue;
01725       }
01726       if (name.count() > 1) {
01727         Note("Multiple values for 'Name' attribute in %s; using the first one", xobj->object_name());
01728       }
01729       if (format.count() > 1) {
01730         Note("Multiple values for 'Format' attribute in %s; using the first one", xobj->object_name());
01731       }
01732 
01733       char *format_str = format.dequeue();
01734       char *name_str = name.dequeue();
01735       unsigned interval_num = 0;
01736 
01737       // if the format_str contains any of the aggregate operators,
01738       // we need to ensure that an interval was specified.
01739       //
01740       if (LogField::fieldlist_contains_aggregates(format_str)) {
01741         if (interval.count() == 0) {
01742           Note("'Interval' attribute missing for LogFormat object"
01743                " %s that contains aggregate operators: %s", name_str, format_str);
01744           continue;
01745         } else if (interval.count() > 1) {
01746           Note("Multiple values for 'Interval' attribute in %s; using the first one", xobj->object_name());
01747         }
01748         // interval
01749         //
01750         interval_num = ink_atoui(interval.dequeue());
01751       } else if (interval.count() > 0) {
01752         Note("Format %s has no aggregates, ignoring 'Interval' attribute.", name_str);
01753       }
01754       // create new format object and place onto global list
01755       //
01756       LogFormat *fmt = new LogFormat(name_str, format_str, interval_num);
01757 
01758       ink_assert(fmt != NULL);
01759       if (fmt->valid()) {
01760         global_format_list.add(fmt, false);
01761 
01762         if (is_debug_tag_set("xml")) {
01763           printf("The following format was added to the global format list\n");
01764           fmt->displayAsXML(stdout);
01765         }
01766       } else {
01767         Note("Format named \"%s\" will not be active; not a valid format", fmt->name()? fmt->name() : "");
01768         delete fmt;
01769       }
01770     }
01771 
01772     else if (strcmp(xobj->object_name(), "LogFilter") == 0) {
01773       int i;
01774 
01775       // Mandatory attributes: Name, Action, Condition
01776       // Optional attributes : none
01777       //
01778       NameList name;
01779       NameList condition;
01780       NameList action;
01781 
01782       for (xattr = xobj->first(); xattr; xattr = xobj->next(xattr)) {
01783         Debug("xml", "XmlAttr  : <%s,%s>", xattr->tag(), xattr->value());
01784 
01785         if (strcasecmp(xattr->tag(), "Name") == 0) {
01786           name.enqueue(xattr->value());
01787         } else if (strcasecmp(xattr->tag(), "Action") == 0) {
01788           action.enqueue(xattr->value());
01789         } else if (strcasecmp(xattr->tag(), "Condition") == 0) {
01790           condition.enqueue(xattr->value());
01791         } else {
01792           Note("Unknown attribute %s for %s; ignoring", xattr->tag(), xobj->object_name());
01793         }
01794       }
01795 
01796       // check integrity constraints
01797       //
01798       if (name.count() == 0) {
01799         Note("'Name' attribute missing for LogFilter object");
01800         continue;
01801       }
01802       if (action.count() == 0) {
01803         Note("'Action' attribute missing for LogFilter object");
01804         continue;
01805       }
01806       if (condition.count() == 0) {
01807         Note("'Condition' attribute missing for LogFilter object");
01808         continue;
01809       }
01810       if (name.count() > 1) {
01811         Note("Multiple values for 'Name' attribute in %s; using the first one", xobj->object_name());
01812       }
01813       if (action.count() > 1) {
01814         Note("Multiple values for 'Action' attribute in %s; using the first one", xobj->object_name());
01815       }
01816       if (condition.count() > 1) {
01817         Note("Multiple values for 'Condition' attribute in %s; using the first one", xobj->object_name());
01818       }
01819 
01820       char *filter_name = name.dequeue();
01821 
01822       // convert the action string to an enum value and validate it
01823       //
01824       char *action_str = action.dequeue();
01825       LogFilter::Action act = LogFilter::REJECT;        /* lv: make gcc happy */
01826       for (i = 0; i < LogFilter::N_ACTIONS; i++) {
01827         if (strcasecmp(action_str, LogFilter::ACTION_NAME[i]) == 0) {
01828           act = (LogFilter::Action) i;
01829           break;
01830         }
01831       }
01832 
01833       if (i == LogFilter::N_ACTIONS) {
01834         Warning("%s is not a valid filter action value; cannot create filter %s.", action_str, filter_name);
01835         continue;
01836       }
01837       // parse condition string and validate its fields
01838       //
01839       char *cond_str = condition.dequeue();
01840 
01841       SimpleTokenizer tok(cond_str);
01842 
01843       if (tok.getNumTokensRemaining() < 3) {
01844         Warning("Invalid condition syntax \"%s\"; cannot create filter %s.", cond_str, filter_name);
01845         continue;
01846       }
01847 
01848       char *field_str = tok.getNext();
01849       char *oper_str = tok.getNext();
01850       char *val_str = tok.getRest();
01851 
01852       // validate field symbol
01853       //
01854       if (strlen(field_str) > 2 && field_str[0] == '%' && field_str[1] == '<') {
01855         Debug("xml", "Field symbol has <> form: %s", field_str);
01856         char *end = field_str;
01857         while (*end && *end != '>')
01858           end++;
01859         *end = '\0';
01860         field_str += 2;
01861         Debug("xml", "... now field symbol is %s", field_str);
01862       }
01863 
01864       LogField *logfield = Log::global_field_list.find_by_symbol(field_str);
01865       if (!logfield) {
01866         // check for container fields
01867         if (*field_str == '{') {
01868           Note("%s appears to be a container field", field_str);
01869           char *fname_end = strchr(field_str, '}');
01870           if (NULL != fname_end) {
01871             char *fname = field_str + 1;
01872             *fname_end = 0;          // changes '}' to '\0'
01873             char *cname = fname_end + 1;     // start of container symbol
01874             Note("Found Container Field: Name = %s, symbol = %s", fname, cname);
01875             LogField::Container container = LogField::valid_container_name(cname);
01876             if (container == LogField::NO_CONTAINER) {
01877               Warning("%s is not a valid container; cannot create filter %s.", cname, filter_name);
01878               continue;
01879             } else {
01880               logfield = new LogField(fname, container);
01881               ink_assert(logfield != NULL);
01882             }
01883           } else {
01884             Warning("Invalid container field specification: no trailing '}' in %s cannot create filter %s.", field_str, filter_name);
01885             continue;
01886           }
01887         }
01888       }
01889 
01890       if (!logfield) {
01891         Warning("%s is not a valid field; cannot create filter %s.", field_str, filter_name);
01892         continue;
01893       }
01894       // convert the operator string to an enum value and validate it
01895       //
01896       LogFilter::Operator oper = LogFilter::MATCH;
01897       for (i = 0; i < LogFilter::N_OPERATORS; ++i) {
01898         if (strcasecmp(oper_str, LogFilter::OPERATOR_NAME[i]) == 0) {
01899           oper = (LogFilter::Operator) i;
01900           break;
01901         }
01902       }
01903 
01904       if (i == LogFilter::N_OPERATORS) {
01905         Warning("%s is not a valid operator; cannot create filter %s.", oper_str, filter_name);
01906         continue;
01907       }
01908       // now create the correct LogFilter
01909       //
01910       LogFilter *filter = NULL;
01911       LogField::Type field_type = logfield->type();
01912 
01913       switch (field_type) {
01914 
01915       case LogField::sINT:
01916 
01917         filter = new LogFilterInt(filter_name, logfield, act, oper, val_str);
01918         break;
01919 
01920       case LogField::dINT:
01921 
01922         Warning("Internal error: invalid field type (double int); cannot create filter %s.", filter_name);
01923         continue;
01924 
01925       case LogField::STRING:
01926 
01927         filter = new LogFilterString(filter_name, logfield, act, oper, val_str);
01928         break;
01929 
01930       case LogField::IP:
01931 
01932         filter = new LogFilterIP(filter_name, logfield, act, oper, val_str);
01933         break;
01934 
01935       default:
01936 
01937         Warning("Internal error: unknown field type %d; cannot create filter %s.", field_type, filter_name);
01938         continue;
01939       }
01940 
01941       ink_assert(filter);
01942 
01943       if (filter->get_num_values() == 0) {
01944 
01945         Warning("\"%s\" does not specify any valid values; cannot create filter %s.", val_str, filter_name);
01946         delete filter;
01947 
01948       } else {
01949 
01950         // add filter to global filter list
01951         //
01952         global_filter_list.add(filter, false);
01953 
01954         if (is_debug_tag_set("xml")) {
01955           printf("The following filter was added to the global filter list\n");
01956           filter->display_as_XML();
01957         }
01958       }
01959     }
01960 
01961     else if (strcasecmp(xobj->object_name(), "LogObject") == 0) {
01962 
01963       NameList format;          // mandatory
01964       NameList filename;        // mandatory
01965       NameList mode;
01966       NameList filters;
01967       NameList protocols;
01968       NameList serverHosts;
01969       NameList collationHosts;
01970       NameList header;
01971       NameList rollingEnabled;
01972       NameList rollingIntervalSec;
01973       NameList rollingOffsetHr;
01974       NameList rollingSizeMb;
01975 
01976       for (xattr = xobj->first(); xattr; xattr = xobj->next(xattr)) {
01977         Debug("xml", "XmlAttr  : <%s,%s>", xattr->tag(), xattr->value());
01978         if (strcasecmp(xattr->tag(), "Format") == 0) {
01979           format.enqueue(xattr->value());
01980         } else if (strcasecmp(xattr->tag(), "Filename") == 0) {
01981           filename.enqueue(xattr->value());
01982         } else if (strcasecmp(xattr->tag(), "Mode") == 0) {
01983           mode.enqueue(xattr->value());
01984         } else if (strcasecmp(xattr->tag(), "Filters") == 0) {
01985           filters.enqueue(xattr->value());
01986         } else if (strcasecmp(xattr->tag(), "Protocols") == 0) {
01987           protocols.enqueue(xattr->value());
01988         } else if (strcasecmp(xattr->tag(), "ServerHosts") == 0) {
01989           serverHosts.enqueue(xattr->value());
01990         } else if (strcasecmp(xattr->tag(), "CollationHosts") == 0) {
01991           collationHosts.enqueue(xattr->value());
01992         } else if (strcasecmp(xattr->tag(), "Header") == 0) {
01993           header.enqueue(xattr->value());
01994         } else if (strcasecmp(xattr->tag(), "RollingEnabled") == 0) {
01995           rollingEnabled.enqueue(xattr->value());
01996         } else if (strcasecmp(xattr->tag(), "RollingIntervalSec") == 0) {
01997           rollingIntervalSec.enqueue(xattr->value());
01998         } else if (strcasecmp(xattr->tag(), "RollingOffsetHr") == 0) {
01999           rollingOffsetHr.enqueue(xattr->value());
02000         } else if (strcasecmp(xattr->tag(), "RollingSizeMb") == 0) {
02001           rollingSizeMb.enqueue(xattr->value());
02002         } else {
02003           Note("Unknown attribute %s for %s; ignoring", xattr->tag(), xobj->object_name());
02004         }
02005       }
02006 
02007       // check integrity constraints
02008       //
02009       if (format.count() == 0) {
02010         Note("'Format' attribute missing for LogObject object");
02011         continue;
02012       }
02013       if (filename.count() == 0) {
02014         Note("'Filename' attribute missing for LogObject object");
02015         continue;
02016       }
02017 
02018       if (format.count() > 1) {
02019         Note("Multiple values for 'Format' attribute in %s; using the first one", xobj->object_name());
02020       }
02021       if (filename.count() > 1) {
02022         Note("Multiple values for 'Filename' attribute in %s; using the first one", xobj->object_name());
02023       }
02024       if (mode.count() > 1) {
02025         Note("Multiple values for 'Mode' attribute in %s; using the first one", xobj->object_name());
02026       }
02027       if (filters.count() > 1) {
02028         Note("Multiple values for 'Filters' attribute in %s; using the first one", xobj->object_name());
02029       }
02030       if (protocols.count() > 1) {
02031         Note("Multiple values for 'Protocols' attribute in %s; using the first one", xobj->object_name());
02032       }
02033       if (serverHosts.count() > 1) {
02034         Note("Multiple values for 'ServerHosts' attribute in %s; using the first one", xobj->object_name());
02035       }
02036       if (collationHosts.count() > 1) {
02037         Note("Multiple values for 'CollationHosts' attribute in %s; using the first one", xobj->object_name());
02038       }
02039       if (header.count() > 1) {
02040         Note("Multiple values for 'Header' attribute in %s; using the first one", xobj->object_name());
02041       }
02042       if (rollingEnabled.count() > 1) {
02043         Note("Multiple values for 'RollingEnabled' attribute in %s; using the first one", xobj->object_name());
02044       }
02045       if (rollingIntervalSec.count() > 1) {
02046         Note("Multiple values for 'RollingIntervalSec' attribute in %s; using the first one", xobj->object_name());
02047       }
02048       if (rollingOffsetHr.count() > 1) {
02049         Note("Multiple values for 'RollingOffsetHr' attribute in %s; using the first one", xobj->object_name());
02050       }
02051       if (rollingSizeMb.count() > 1) {
02052         Note("Multiple values for 'RollingSizeMb' attribute in %s; using the first one", xobj->object_name());
02053       }
02054       // create new LogObject and start adding to it
02055       //
02056 
02057       char *fmt_name = format.dequeue();
02058       LogFormat *fmt = global_format_list.find_by_name(fmt_name);
02059       if (!fmt) {
02060         Warning("Format %s not in the global format list; cannot create LogObject", fmt_name);
02061         continue;
02062       }
02063       // file format
02064       //
02065       LogFileFormat file_type = LOG_FILE_ASCII;      // default value
02066       if (mode.count()) {
02067         char *mode_str = mode.dequeue();
02068         file_type = (strncasecmp(mode_str, "bin", 3) == 0 ||
02069                      (mode_str[0] == 'b' && mode_str[1] == 0) ?
02070                      LOG_FILE_BINARY : (strcasecmp(mode_str, "ascii_pipe") == 0 ? LOG_FILE_PIPE : LOG_FILE_ASCII));
02071       }
02072       // rolling
02073       //
02074       char *rollingEnabled_str = rollingEnabled.dequeue();
02075       int obj_rolling_enabled = rollingEnabled_str ? ink_atoui(rollingEnabled_str) : rolling_enabled;
02076 
02077       char *rollingIntervalSec_str = rollingIntervalSec.dequeue();
02078       int obj_rolling_interval_sec = rollingIntervalSec_str ? ink_atoui(rollingIntervalSec_str) : rolling_interval_sec;
02079 
02080       char *rollingOffsetHr_str = rollingOffsetHr.dequeue();
02081       int obj_rolling_offset_hr = rollingOffsetHr_str ? ink_atoui(rollingOffsetHr_str) : rolling_offset_hr;
02082 
02083       char *rollingSizeMb_str = rollingSizeMb.dequeue();
02084       int obj_rolling_size_mb = rollingSizeMb_str ? ink_atoui(rollingSizeMb_str) : rolling_size_mb;
02085 
02086       if (!LogRollingEnabledIsValid(obj_rolling_enabled)) {
02087         Warning("Invalid log rolling value '%d' in log object %s", obj_rolling_enabled, xobj->object_name());
02088       }
02089 
02090       // create the new object
02091       //
02092       LogObject *obj = new LogObject(fmt, logfile_dir,
02093                                      filename.dequeue(),
02094                                      file_type,
02095                                      header.dequeue(),
02096                                      (Log::RollingEnabledValues)obj_rolling_enabled,
02097                                      collation_preproc_threads,
02098                                      obj_rolling_interval_sec,
02099                                      obj_rolling_offset_hr,
02100                                      obj_rolling_size_mb);
02101 
02102       // filters
02103       //
02104       char *filters_str = filters.dequeue();
02105       if (filters_str) {
02106         SimpleTokenizer tok(filters_str, ',');
02107         char *filter_name;
02108         while (filter_name = tok.getNext(), filter_name != 0) {
02109           LogFilter *f;
02110           f = global_filter_list.find_by_name(filter_name);
02111           if (!f) {
02112             Warning("Filter %s not in the global filter list; cannot add to this LogObject", filter_name);
02113           } else {
02114             obj->add_filter(f);
02115           }
02116         }
02117       }
02118       // protocols
02119       //
02120       char *protocols_str = protocols.dequeue();
02121       if (protocols_str) {
02122 
02123         LogField *etype_field = Log::global_field_list.find_by_symbol("etype");
02124         ink_assert(etype_field);
02125 
02126         SimpleTokenizer tok(protocols_str, ',');
02127         size_t n = tok.getNumTokensRemaining();
02128 
02129         if (n) {
02130           int64_t *val_array = new int64_t[n];
02131           size_t numValid = 0;
02132           char *t;
02133           while (t = tok.getNext(), t != NULL) {
02134             if (strcasecmp(t, "icp") == 0) {
02135               val_array[numValid++] = LOG_ENTRY_ICP;
02136             } else if (strcasecmp(t, "http") == 0) {
02137               val_array[numValid++] = LOG_ENTRY_HTTP;
02138             }
02139           }
02140 
02141           if (numValid == 0) {
02142             Warning("No valid protocol value(s) (%s) for Protocol "
02143                     "field in definition of XML LogObject.\nObject will log all protocols.", protocols_str);
02144           } else {
02145             if (numValid < n) {
02146               Warning("There are invalid protocol values (%s) in"
02147                       " the Protocol field of XML LogObject.\n"
02148                       "Only %zu out of %zu values will be used.", protocols_str, numValid, n);
02149             }
02150 
02151             LogFilterInt protocol_filter("__xml_protocol__",
02152                                          etype_field, LogFilter::ACCEPT, LogFilter::MATCH, numValid, val_array);
02153             obj->add_filter(&protocol_filter);
02154           }
02155           delete[] val_array;
02156         } else {
02157           Warning("No value(s) in Protocol field of XML object, object will log all protocols.");
02158         }
02159       }
02160       // server hosts
02161       //
02162       char *serverHosts_str = serverHosts.dequeue();
02163       if (serverHosts_str) {
02164 
02165         LogField *shn_field = Log::global_field_list.find_by_symbol("shn");
02166         ink_assert(shn_field);
02167 
02168         LogFilterString server_host_filter("__xml_server_hosts__",
02169                                            shn_field, LogFilter::ACCEPT, LogFilter::CASE_INSENSITIVE_CONTAIN, serverHosts_str);
02170 
02171         if (server_host_filter.get_num_values() == 0) {
02172           Warning("No valid server host value(s) (%s) for Protocol "
02173                   "field in definition of XML LogObject.\nObject will log all servers.", serverHosts_str);
02174         } else {
02175           obj->add_filter(&server_host_filter);
02176         }
02177       }
02178       // collation hosts
02179       //
02180       char *collationHosts_str = collationHosts.dequeue();
02181       if (collationHosts_str) {
02182         char *host;
02183         SimpleTokenizer tok(collationHosts_str, ',');
02184         while (host = tok.getNext(), host != 0) {
02185 
02186           LogHost *prev = NULL;
02187           char *failover_str;
02188           SimpleTokenizer failover_tok(host, '|'); // split failover hosts
02189 
02190           while (failover_str = failover_tok.getNext(), failover_str != 0) {
02191             LogHost *lh = new LogHost(obj->get_full_filename(), obj->get_signature());
02192 
02193             if (lh->set_name_or_ipstr(failover_str)) {
02194               Warning("Could not set \"%s\" as collation host", host);
02195               delete lh;
02196             } else if (!prev){
02197               obj->add_loghost(lh, false);
02198               prev = lh;
02199             } else {
02200               prev->failover_link.next = lh;
02201               prev = lh;
02202             }
02203           }
02204         }
02205       }
02206       // now the object is complete; give it to the object manager
02207       //
02208       log_object_manager.manage_object(obj);
02209     }
02210 
02211     else {
02212       Note("Unknown XML config object for logging: %s", xobj->object_name());
02213     }
02214   }
02215 }
02216 
02217 /*-------------------------------------------------------------------------
02218   LogConfig::read_log_hosts_file
02219 
02220   This routine will read the log_hosts.config file to build a set of
02221   filters for splitting logs based on hostname fields that match the
02222   entries in this file.
02223   -------------------------------------------------------------------------*/
02224 
02225 char **
02226 LogConfig::read_log_hosts_file(size_t * num_hosts)
02227 {
02228   ats_scoped_str config_path(Layout::get()->relative_to(Layout::get()->sysconfdir, hosts_config_file));
02229   char line[LOG_MAX_FORMAT_LINE];
02230   char **hosts = NULL;
02231 
02232   Debug("log-config", "Reading log hosts from %s", (const char *)config_path);
02233 
02234   size_t nhosts = 0;
02235   int fd = open(config_path, O_RDONLY);
02236   if (fd < 0) {
02237     Warning("Traffic Server can't open %s for reading log hosts for splitting: %s.", (const char *)config_path, strerror(errno));
02238   } else {
02239     //
02240     // First, count the number of hosts in the file
02241     //
02242     while (ink_file_fd_readline(fd, LOG_MAX_FORMAT_LINE, line) > 0) {
02243       //
02244       // Ignore blank Lines and lines that begin with a '#'.
02245       //
02246       if (*line == '\n' || *line == '#') {
02247         continue;
02248       }
02249       ++nhosts;
02250     }
02251     //
02252     // Now read the hosts from the file and set-up the array entries.
02253     //
02254     if (nhosts) {
02255       if (lseek(fd, 0, SEEK_SET) != 0) {
02256         Warning("lseek failed on file %s: %s", (const char *)config_path, strerror(errno));
02257         nhosts = 0;
02258       } else {
02259         hosts = new char *[nhosts];
02260         ink_assert(hosts != NULL);
02261 
02262         size_t i = 0;
02263         while (ink_file_fd_readline(fd, LOG_MAX_FORMAT_LINE, line) > 0) {
02264           //
02265           // Ignore blank Lines and lines that begin with a '#'.
02266           //
02267           if (*line == '\n' || *line == '#') {
02268             continue;
02269           }
02270           LogUtils::strip_trailing_newline(line);
02271           hosts[i] = ats_strdup(line);
02272           ++i;
02273         }
02274         ink_assert(i == nhosts);
02275       }
02276     }
02277     close(fd);
02278   }
02279   *num_hosts = nhosts;
02280   return hosts;
02281 }

Generated by  doxygen 1.7.1