This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 35476688cd314041263915f5376d6222b1984be0
Author: dyrock <zeyu...@gmail.com>
AuthorDate: Wed Oct 24 13:59:13 2018 -0500

    Tested locally
---
 doc/admin-guide/files/logging.yaml.en.rst   |   2 +
 doc/admin-guide/files/records.config.en.rst |  30 ++++++--
 doc/admin-guide/logging/rotation.en.rst     |  10 ++-
 mgmt/RecordsConfig.cc                       |   6 ++
 proxy/logging/LogConfig.cc                  | 112 ++++++++++++++++++++--------
 proxy/logging/LogConfig.h                   |  84 ++++++++++++++++++---
 proxy/logging/YamlLogConfig.cc              |  26 ++++++-
 7 files changed, 217 insertions(+), 53 deletions(-)

diff --git a/doc/admin-guide/files/logging.yaml.en.rst 
b/doc/admin-guide/files/logging.yaml.en.rst
index 670a492..10bc404 100644
--- a/doc/admin-guide/files/logging.yaml.en.rst
+++ b/doc/admin-guide/files/logging.yaml.en.rst
@@ -313,6 +313,8 @@ rolling_offset_hr      number      Specifies an hour (from 
0 to 23) at which log
                                    :ts:cv:`proxy.config.log.rolling_offset_hr`.
 rolling_size_mb        number      Size, in megabytes, at which log files are
                                    rolled.
+rolling_min_count      number      Specifies the minimum number of rolled logs 
to
+                                   keep.
 filters                array of    The optional list of filter objects which
                        filters     restrict the individual events logged. The 
array
                                    may only contain one accept filter.
diff --git a/doc/admin-guide/files/records.config.en.rst 
b/doc/admin-guide/files/records.config.en.rst
index cc0c46b..c773e97 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -272,6 +272,12 @@ System Variables
 
    Specifies at what size to roll the output log at.
 
+.. ts:cv:: CONFIG proxy.config.output.logfile.rolling_min_count INT 0
+   :reloadable:
+
+   Specifies the minimum count of rolled output logs to keep. This value will 
be used to decide the
+   order of auto-deletion (if enabled). A default value of 0 means 
auto-deletion will try to keep
+   output logs as much as possible.
 
 Thread Variables
 ----------------
@@ -2950,6 +2956,13 @@ Logging Configuration
    The size, in megabytes, that log files must reach before rolling takes 
place.
    The minimum value for this setting is ``10``.
 
+.. ts:cv:: CONFIG proxy.config.log.rolling_min_count INT 0
+   :reloadable:
+
+   Specifies the minimum count of rolled (event) logs to keep. This value will 
be used to decide the
+   order of auto-deletion (if enabled). A default value of 0 means 
auto-deletion will try to keep
+   logs as much as possible. This value can be and should be overridden in 
logging.yaml.
+
 .. ts:cv:: CONFIG proxy.config.log.auto_delete_rolled_files INT 1
    :reloadable:
 
@@ -3112,6 +3125,13 @@ Diagnostic Logging Configuration
 
    Specifies at what size to roll the diagnostics log at.
 
+.. ts:cv:: CONFIG proxy.config.diags.logfile.rolling_min_count INT 0
+   :reloadable:
+
+   Specifies the minimum count of rolled diagnostic logs to keep. This value 
will be used to decide the
+   order of auto-deletion (if enabled). A default value of 0 means 
auto-deletion will try to keep
+   diagnostic logs as much as possible.
+
 Reverse Proxy
 =============
 
@@ -3457,11 +3477,11 @@ Client-Related Configuration
 
    You can override this global setting on a per domain basis in the 
ssl_servername.yaml file using the :ref:`verify_server_policy 
attribute<override-verify-server-policy>`.
 
-:code:`DISABLED` 
+:code:`DISABLED`
    Server Certificate will not be verified
-:code:`PERMISSIVE` 
+:code:`PERMISSIVE`
    Certificate will be verified and the connection will not be established if 
verification fails.
-:code:`ENFORCED` 
+:code:`ENFORCED`
    The provided certificate will be verified and the connection will be 
established irrespective of the verification result. If verification fails the 
name of the server will be logged.
 
 .. ts:cv:: CONFIG proxy.config.ssl.client.verify.server.properties STRING ALL
@@ -3494,7 +3514,7 @@ Client-Related Configuration
 
    :0: Server Certificate will not be verified
    :1: Certificate will be verified and the connection will not be established 
if verification fail
-   :2: The provided certificate will be verified and the connection will be 
established 
+   :2: The provided certificate will be verified and the connection will be 
established
 
 .. ts:cv:: CONFIG proxy.config.ssl.client.cert.filename STRING NULL
    :reloadable:
@@ -3930,7 +3950,7 @@ Sockets
 
         Reduce the number of worker threads (net-threads)
         Reduce the number of disk (AIO) threads
-       Make sure accept threads are enabled
+   Make sure accept threads are enabled
 
    The relevant configurations for this are::
 
diff --git a/doc/admin-guide/logging/rotation.en.rst 
b/doc/admin-guide/logging/rotation.en.rst
index 94f25e6..5e01242 100644
--- a/doc/admin-guide/logging/rotation.en.rst
+++ b/doc/admin-guide/logging/rotation.en.rst
@@ -167,6 +167,11 @@ they reach a certain size, adjust the following settings in
 
     CONFIG proxy.config.log.rolling_interval_sec INT 21600
 
+#. Set the minimum number of rolled files with
+   :ts:cv:`proxy.config.log.rolling_min_count`. ::
+
+    CONFIG proxy.config.log.rolling_min_count INT 0
+
 #. Run the command :option:`traffic_ctl config reload` to apply the 
configuration
    changes.
 
@@ -190,8 +195,9 @@ following actions:
 
 -  If the autodelete option is enabled, then |TS| identifies previously-rolled
    log files (log files with the ``.old`` extension). It starts deleting files
-   one by one, beginning with the oldest file, until it emerges from the low
-   state. |TS| logs a record of all deleted files in the system error log.
+   one by one, beginning with the oldest file with largest ratio to the minimum
+   rolling count, until it emerges from the low state. |TS| logs a record of 
all
+   deleted files in the system error log.
 
 -  If the autodelete option is disabled or there are not enough old log files
    to delete for the system to emerge from its low space state, then |TS|
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index a03324f..b28e330 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -95,6 +95,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.output.logfile.rolling_size_mb", RECD_INT, 
"100", RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.output.logfile.rolling_min_count", RECD_INT, 
"0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
+  ,
   //# 0 = disable
   {RECT_CONFIG, "proxy.config.res_track_memory", RECD_INT, "0", 
RECU_RESTART_TS, RR_REQUIRED, RECC_INT,  "[0-2]", RECA_NULL}
   ,
@@ -230,6 +232,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.diags.logfile.rolling_size_mb", RECD_INT, "10", 
RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.diags.logfile.rolling_min_count", RECD_INT, "0", 
RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
+  ,
 
   
//##############################################################################
   //#
@@ -1044,6 +1048,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.log.rolling_size_mb", RECD_INT, "10", 
RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.log.rolling_min_count", RECD_INT, "0", 
RECU_DYNAMIC, RR_NULL, RECC_STR, "^0*[1-9][0-9]*$", RECA_NULL}
+  ,
   {RECT_CONFIG, "proxy.config.log.auto_delete_rolled_files", RECD_INT, "1", 
RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
   ,
   {RECT_CONFIG, "proxy.config.log.sampling_frequency", RECD_INT, "1", 
RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
diff --git a/proxy/logging/LogConfig.cc b/proxy/logging/LogConfig.cc
index ce29674..7176855 100644
--- a/proxy/logging/LogConfig.cc
+++ b/proxy/logging/LogConfig.cc
@@ -63,6 +63,7 @@
 #define DISK_IS_ACTUAL_LOW_MESSAGE "Access logging to local log directory 
suspended - partition space is low."
 
 #define PARTITION_HEADROOM_MB 10
+#define DIAGS_LOG_FILENAME "diags.log"
 
 void
 LogConfig::setup_default_values()
@@ -220,8 +221,8 @@ LogConfig::read_configuration_variables()
   rolling_interval_sec = 
(int)REC_ConfigReadInteger("proxy.config.log.rolling_interval_sec");
   rolling_offset_hr    = 
(int)REC_ConfigReadInteger("proxy.config.log.rolling_offset_hr");
   rolling_size_mb      = 
(int)REC_ConfigReadInteger("proxy.config.log.rolling_size_mb");
-
-  val = (int)REC_ConfigReadInteger("proxy.config.log.rolling_enabled");
+  rolling_min_count    = 
(int)REC_ConfigReadInteger("proxy.config.log.rolling_min_count");
+  val                  = 
(int)REC_ConfigReadInteger("proxy.config.log.rolling_enabled");
   if (LogRollingEnabledIsValid(val)) {
     rolling_enabled = (Log::RollingEnabledValues)val;
   } else {
@@ -232,6 +233,23 @@ LogConfig::read_configuration_variables()
   val                      = 
(int)REC_ConfigReadInteger("proxy.config.log.auto_delete_rolled_files");
   auto_delete_rolled_files = (val > 0);
 
+  // Read in min_count control values for auto deletion
+  if (auto_delete_rolled_files) {
+    // For diagnostic logs
+    val = 
(int)REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_min_count");
+    val = ((val == 0) ? INT_MAX : val);
+    deleting_info.insert(new LogDeletingInfo(DIAGS_LOG_FILENAME, val));
+
+    // For traffic.out
+    ats_scoped_str name(REC_ConfigReadString("proxy.config.output.logfile"));
+    val = 
(int)REC_ConfigReadInteger("proxy.config.output.logfile.rolling_min_count");
+    val = ((val == 0) ? INT_MAX : val);
+    if (name) {
+      deleting_info.insert(new LogDeletingInfo(name.get(), val));
+    } else {
+      deleting_info.insert(new LogDeletingInfo("traffic.out", val));
+    }
+  }
   // PERFORMANCE
   val = (int)REC_ConfigReadInteger("proxy.config.log.sampling_frequency");
   if (val > 0) {
@@ -697,12 +715,6 @@ LogConfig::space_to_write(int64_t bytes_to_write) const
   periodic space check.
   -------------------------------------------------------------------------*/
 
-static int
-delete_candidate_compare(const LogDeleteCandidate *a, const LogDeleteCandidate 
*b)
-{
-  return ((int)(a->mtime - b->mtime));
-}
-
 void
 LogConfig::update_space_used()
 {
@@ -712,9 +724,7 @@ LogConfig::update_space_used()
     return;
   }
 
-  static const int MAX_CANDIDATES = 128;
-  LogDeleteCandidate candidates[MAX_CANDIDATES];
-  int i, victim, candidate_count;
+  int candidate_count;
   int64_t total_space_used, partition_space_left;
   char path[MAXPATHLEN];
   int sret;
@@ -758,6 +768,9 @@ LogConfig::update_space_used()
   total_space_used = 0LL;
   candidate_count  = 0;
 
+  for (auto it = deleting_info.begin(); it != deleting_info.end(); it++) {
+    Debug("%s", it->name.c_str());
+  }
   while ((entry = readdir(ld))) {
     snprintf(path, MAXPATHLEN, "%s/%s", logfile_dir, entry->d_name);
 
@@ -765,13 +778,21 @@ LogConfig::update_space_used()
     if (sret != -1 && S_ISREG(sbuf.st_mode)) {
       total_space_used += (int64_t)sbuf.st_size;
 
-      if (auto_delete_rolled_files && LogFile::rolled_logfile(entry->d_name) 
&& candidate_count < MAX_CANDIDATES) {
+      if (auto_delete_rolled_files && LogFile::rolled_logfile(entry->d_name)) {
         //
-        // then add this entry to the candidate list
+        // then check if the candidate belongs to any given log type
         //
-        candidates[candidate_count].name  = ats_strdup(path);
-        candidates[candidate_count].size  = (int64_t)sbuf.st_size;
-        candidates[candidate_count].mtime = sbuf.st_mtime;
+        ts::TextView type_name(entry->d_name, strlen(entry->d_name));
+        auto suffix = type_name;
+        type_name.remove_suffix(suffix.remove_prefix(suffix.find('.') + 
1).remove_prefix(suffix.find('.')).size());
+        auto iter = deleting_info.find(type_name);
+        if (iter == deleting_info.end()) {
+          // We won't delete the log if its name doesn't match any give type.
+          break;
+        }
+
+        auto &candidates = iter->candidates;
+        candidates.push_back(LogDeleteCandidate(path, (int64_t)sbuf.st_size, 
sbuf.st_mtime));
         candidate_count++;
       }
     }
@@ -818,36 +839,61 @@ LogConfig::update_space_used()
   if (candidate_count > 0 && !space_to_write(headroom)) {
     Debug("logspace", "headroom reached, trying to clear space ...");
     Debug("logspace", "sorting %d delete candidates ...", candidate_count);
-    qsort(candidates, candidate_count, sizeof(LogDeleteCandidate), (int 
(*)(const void *, const void *))delete_candidate_compare);
 
-    for (victim = 0; victim < candidate_count; victim++) {
+    deleting_info.apply([](LogDeletingInfo &info) {
+      std::sort(info.candidates.begin(), info.candidates.end(),
+                [](LogDeleteCandidate const &a, LogDeleteCandidate const &b) { 
return a.mtime > b.mtime; });
+    });
+
+    while (candidate_count > 0) {
       if (space_to_write(headroom + log_buffer_size)) {
         Debug("logspace", "low water mark reached; stop deleting");
         break;
       }
 
-      Debug("logspace", "auto-deleting %s", candidates[victim].name);
-
-      if (unlink(candidates[victim].name) < 0) {
-        Note("Traffic Server was Unable to auto-delete rolled "
-             "logfile %s: %s.",
-             candidates[victim].name, strerror(errno));
+      // Select the group with biggest ratio
+      auto target =
+        std::max_element(deleting_info.begin(), deleting_info.end(), 
[](LogDeletingInfo const &A, LogDeletingInfo const &B) {
+          double diff =
+            static_cast<double>(A.candidates.size()) / A.min_count - 
static_cast<double>(B.candidates.size()) / B.min_count;
+          return diff < 0.0;
+        });
+
+      auto &candidates = target->candidates;
+
+      // Check if any candidate exists
+      if (candidates.empty()) {
+        // This shouldn't be triggered unless min_count are configured wrong 
or extra non-log files occupy the directory
+        Debug("logspace", "No more victims for log type %s. Check your 
rolling_min_count settings and logging directory.",
+              target->name.c_str());
       } else {
-        Debug("logspace",
-              "The rolled logfile, %s, was auto-deleted; "
-              "%" PRId64 " bytes were reclaimed.",
-              candidates[victim].name, candidates[victim].size);
-        m_space_used -= candidates[victim].size;
-        m_partition_space_left += candidates[victim].size;
+        auto &victim = candidates.back();
+        Debug("logspace", "auto-deleting %s", victim.name.c_str());
+
+        if (unlink(victim.name.c_str()) < 0) {
+          Note("Traffic Server was Unable to auto-delete rolled "
+               "logfile %s: %s.",
+               victim.name.c_str(), strerror(errno));
+        } else {
+          Debug("logspace",
+                "The rolled logfile, %s, was auto-deleted; "
+                "%" PRId64 " bytes were reclaimed.",
+                victim.name.c_str(), victim.size);
+
+          // Update after successful unlink;
+          m_space_used -= victim.size;
+          m_partition_space_left += victim.size;
+        }
+        // Update total candidates and remove victim
+        --candidate_count;
+        candidates.pop_back();
       }
     }
   }
   //
   // Clean up the candidate array
   //
-  for (i = 0; i < candidate_count; i++) {
-    ats_free(candidates[i].name);
-  }
+  deleting_info.apply([](LogDeletingInfo &info) { info.clear(); });
 
   //
   // Now that we've updated the m_space_used value, see if we need to
diff --git a/proxy/logging/LogConfig.h b/proxy/logging/LogConfig.h
index 1920aa4..d8efcf3 100644
--- a/proxy/logging/LogConfig.h
+++ b/proxy/logging/LogConfig.h
@@ -23,6 +23,10 @@
 
 #pragma once
 
+#include <string_view>
+#include <string>
+
+#include "tscore/IntrusiveHashMap.h"
 #include "tscore/ink_platform.h"
 #include "records/P_RecProcess.h"
 #include "ProxyConfig.h"
@@ -75,6 +79,73 @@ struct dirent;
 struct LogCollationAccept;
 
 /*-------------------------------------------------------------------------
+  LogDeleteCandidate, LogDeletingInfo&Descriptor
+  -------------------------------------------------------------------------*/
+
+struct LogDeleteCandidate {
+  std::string name;
+  int64_t size;
+  time_t mtime;
+
+  LogDeleteCandidate(char *p_name, int64_t st_size, time_t st_time) : 
name(p_name), size(st_size), mtime(st_time) {}
+};
+
+struct LogDeletingInfo {
+  std::string name;
+  int min_count;
+  std::vector<LogDeleteCandidate> candidates;
+
+  LogDeletingInfo *_next{nullptr};
+  LogDeletingInfo *_prev{nullptr};
+
+  LogDeletingInfo(const char *type, int limit) : name(type), min_count(limit) 
{}
+  LogDeletingInfo(std::string_view type, int limit) : name(type), 
min_count(limit) {}
+
+  void
+  clear()
+  {
+    candidates.clear();
+  }
+};
+
+struct LogDeletingInfoDescriptor {
+  using key_type   = std::string_view;
+  using value_type = LogDeletingInfo;
+
+  static key_type
+  key_of(value_type *value)
+  {
+    return value->name;
+  }
+
+  static bool
+  equal(key_type const &lhs, key_type const &rhs)
+  {
+    return lhs == rhs;
+  }
+
+  static value_type *&
+  next_ptr(value_type *value)
+  {
+    return value->_next;
+  }
+
+  static value_type *&
+  prev_ptr(value_type *value)
+  {
+    return value->_prev;
+  }
+
+  static constexpr std::hash<std::string_view> hasher{};
+
+  static auto
+  hash_of(key_type s) -> decltype(hasher(s))
+  {
+    return hasher(s);
+  }
+};
+
+/*-------------------------------------------------------------------------
   this object keeps the state of the logging configuraion variables.  upon
   construction, the log configuration file is read and the logging
   variables are initialized.
@@ -188,8 +259,11 @@ public:
   int rolling_interval_sec;
   int rolling_offset_hr;
   int rolling_size_mb;
+  int rolling_min_count;
   bool auto_delete_rolled_files;
 
+  IntrusiveHashMap<LogDeletingInfoDescriptor> deleting_info;
+
   int sampling_frequency;
   int file_stat_frequency;
   int space_used_frequency;
@@ -227,13 +301,3 @@ private:
   LogConfig(const LogConfig &) = delete;
   LogConfig &operator=(const LogConfig &) = delete;
 };
-
-/*-------------------------------------------------------------------------
-  LogDeleteCandidate
-  -------------------------------------------------------------------------*/
-
-struct LogDeleteCandidate {
-  time_t mtime;
-  char *name;
-  int64_t size;
-};
diff --git a/proxy/logging/YamlLogConfig.cc b/proxy/logging/YamlLogConfig.cc
index 07bfcfb..b3fd5bc 100644
--- a/proxy/logging/YamlLogConfig.cc
+++ b/proxy/logging/YamlLogConfig.cc
@@ -102,8 +102,8 @@ YamlLogConfig::loadLogConfig(const char *cfgFilename)
 TsEnumDescriptor ROLLING_MODE = {{{"none", 0}, {"time", 1}, {"size", 2}, 
{"both", 3}, {"any", 4}}};
 
 std::set<std::string> valid_log_object_keys = {
-  "filename",          "format",          "mode",    "header",         
"rolling_enabled", "rolling_interval_sec",
-  "rolling_offset_hr", "rolling_size_mb", "filters", "collation_hosts"};
+  "filename",          "format",          "mode",    "header",          
"rolling_enabled", "rolling_interval_sec",
+  "rolling_offset_hr", "rolling_size_mb", "filters", "collation_hosts", 
"min_count"};
 
 LogObject *
 YamlLogConfig::decodeLogObject(const YAML::Node &node)
@@ -149,6 +149,7 @@ YamlLogConfig::decodeLogObject(const YAML::Node &node)
   int obj_rolling_interval_sec = cfg->rolling_interval_sec;
   int obj_rolling_offset_hr    = cfg->rolling_offset_hr;
   int obj_rolling_size_mb      = cfg->rolling_size_mb;
+  int obj_min_count            = cfg->rolling_min_count;
 
   if (node["rolling_enabled"]) {
     auto value          = node["rolling_enabled"].as<std::string>();
@@ -166,7 +167,9 @@ YamlLogConfig::decodeLogObject(const YAML::Node &node)
   if (node["rolling_size_mb"]) {
     obj_rolling_size_mb = node["rolling_size_mb"].as<int>();
   }
-
+  if (node["min_count"]) {
+    obj_min_count = node["min_count"].as<int>();
+  }
   if (!LogRollingEnabledIsValid(obj_rolling_enabled)) {
     Warning("Invalid log rolling value '%d' in log object", 
obj_rolling_enabled);
   }
@@ -175,6 +178,23 @@ YamlLogConfig::decodeLogObject(const YAML::Node &node)
                                  
(Log::RollingEnabledValues)obj_rolling_enabled, 
Log::config->collation_preproc_threads,
                                  obj_rolling_interval_sec, 
obj_rolling_offset_hr, obj_rolling_size_mb);
 
+  // Generate LogDeletingInfo entry for later use
+  std::string ext;
+  switch (file_type) {
+  case LOG_FILE_ASCII:
+    ext = LOG_FILE_ASCII_OBJECT_FILENAME_EXTENSION;
+    break;
+  case LOG_FILE_PIPE:
+    ext = LOG_FILE_PIPE_OBJECT_FILENAME_EXTENSION;
+    break;
+  case LOG_FILE_BINARY:
+    ext = LOG_FILE_BINARY_OBJECT_FILENAME_EXTENSION;
+    break;
+  default:
+    break;
+  }
+  cfg->deleting_info.insert(new LogDeletingInfo(filename + ext, 
((obj_min_count == 0) ? INT_MAX : obj_min_count)));
+
   // filters
   auto filters = node["filters"];
   if (!filters) {

Reply via email to