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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1d2f7dec5b Cleanup log filter code, and add supports for LT and GT 
filters. (#11768)
1d2f7dec5b is described below

commit 1d2f7dec5be7d15acac9ee70bdd7b2994e8f9b94
Author: Leif Hedstrom <[email protected]>
AuthorDate: Wed Oct 16 09:16:45 2024 -0600

    Cleanup log filter code, and add supports for LT and GT filters. (#11768)
    
    * Cleanup of the wipe field filters
    
    * Removes remants of m_does_conjunction
    
    * Adds LT/GT filter logic for integer fields
    
    * Cleanup the actual wipe functionality
    
    * Eliminate the malloc and string copy for uppercase
    
    * Fix milestones type and filter copy ctor
    
    * Adds an example log format and filter for slowlog
    
    * Fix issue with strstrcase string lengths
---
 configs/logging.yaml.default              |  10 +
 doc/admin-guide/files/logging.yaml.en.rst |  16 ++
 include/proxy/logging/LogFilter.h         | 339 ++++------------------
 src/proxy/logging/LogField.cc             |   9 +-
 src/proxy/logging/LogFilter.cc            | 460 +++++++++++++++---------------
 src/proxy/logging/LogObject.cc            |   4 -
 6 files changed, 318 insertions(+), 520 deletions(-)

diff --git a/configs/logging.yaml.default b/configs/logging.yaml.default
index 0612def334..d84fa5b74d 100644
--- a/configs/logging.yaml.default
+++ b/configs/logging.yaml.default
@@ -11,6 +11,13 @@
 
 
 logging:
+# This is an example filter for the slow log, you can tweak and tune this as 
you like for
+# your slowlog debugging. Use this with the slowlog format below, for a 
slowlog file.
+  filters:
+    - name: slowlog
+      action: accept
+      condition: '%<{TS_MILESTONE_SM_FINISH-TS_MILESTONE_SM_START}msdms GTE 
100'
+
   formats:
     # WebTrends Enhanced Log Format.
     #
@@ -42,6 +49,9 @@ logging:
     - name: "extended2"
       format: '%<chi> - %<caun> [%<cqtn>] "%<cqhm> %<pqu> %<cqpv>" %<pssc> 
%<pscl> %<sssc> %<sscl> %<cqcl> %<pqcl> %<cqhl> %<pshl> %<pqhl> %<sshl> %<tts> 
%<phr> %<cfsc> %<pfsc> %<crc>'
 
+    - name: "slowlog"
+      format: '[%<cruuid>] client_ip: %<chi>:%<chp> protocol: %<cqpv> url: 
%<cquc> status: %<pssc> X-ID: %<{X-Id}cqh> bytes=%<pscl> client state: %<cfsc> 
server state: %<sssc> tls_handshake: 
%<{TS_MILESTONE_TLS_HANDSHAKE_END-TS_MILESTONE_TLS_HANDSHAKE_START}msdms> 
ua_begin: %<{TS_MILESTONE_UA_BEGIN-TS_MILESTONE_SM_START}msdms> ua_first_read: 
%<{TS_MILESTONE_UA_FIRST_READ-TS_MILESTONE_SM_START}msdms> ua_read_header_done: 
%<{TS_MILESTONE_UA_READ_HEADER_DONE-TS_MILESTONE_SM_START}msdms> cac [...]
+
   logs:
     - filename: squid
       format: squid
diff --git a/doc/admin-guide/files/logging.yaml.en.rst 
b/doc/admin-guide/files/logging.yaml.en.rst
index 4f46c50611..6bc7d222d3 100644
--- a/doc/admin-guide/files/logging.yaml.en.rst
+++ b/doc/admin-guide/files/logging.yaml.en.rst
@@ -210,6 +210,22 @@ be any one of the following:
     True if the value of ``field`` contains ``value`` (i.e. ``value`` is a
     substring of the contents of ``field``). Case-insensitive.
 
+``LT``
+    True if the value of ``field`` is less than the value of ``value``.
+    Only valid for integer fields.
+
+``LTE``
+    True if the value of ``field`` is less than or equal to the value of
+    ``value``. Only valid for integer fields.
+
+``GT``
+    True if the value of ``field`` is greater than the value of ``value``.
+    Only valid for integer fields.
+
+``GTE``
+    True if the value of ``field`` is less than or equal to the value of
+    ``value``. Only valid for integer fields.
+
 Filter Values
 ~~~~~~~~~~~~~
 
diff --git a/include/proxy/logging/LogFilter.h 
b/include/proxy/logging/LogFilter.h
index 569d33b4d5..a09caee17d 100644
--- a/include/proxy/logging/LogFilter.h
+++ b/include/proxy/logging/LogFilter.h
@@ -66,6 +66,10 @@ public:
     CASE_INSENSITIVE_MATCH,
     CONTAIN,
     CASE_INSENSITIVE_CONTAIN,
+    LT,
+    LTE,
+    GT,
+    GTE,
     N_OPERATORS,
   };
 
@@ -92,8 +96,13 @@ public:
     return m_num_values;
   }
 
+  bool
+  is_wipe() const
+  {
+    return m_action == WIPE_FIELD_VALUE;
+  }
+
   virtual bool toss_this_entry(LogAccess *lad) = 0;
-  virtual bool wipe_this_entry(LogAccess *lad) = 0;
   virtual void display(FILE *fd = stdout)      = 0;
 
   static LogFilter *parse(const char *name, Action action, const char 
*condition);
@@ -133,12 +142,43 @@ public:
   bool operator==(LogFilterString &rhs);
 
   bool toss_this_entry(LogAccess *lad) override;
-  bool wipe_this_entry(LogAccess *lad) override;
   void display(FILE *fd = stdout) override;
 
   // noncopyable
   LogFilterString &operator=(LogFilterString &rhs) = delete;
 
+  // This assumes that s1 is all uppercase, hence we hide this in here 
specifically
+  static const char *
+  strstrcase(const char *s0, const char *s1)
+  {
+    size_t len_s0 = std::strlen(s0);
+    size_t len_s1 = std::strlen(s1);
+
+    if (len_s1 > len_s0) {
+      return nullptr; // If s1 is longer than s0, there's no match
+    }
+
+    const char *end = s0 + len_s0 - len_s1;
+
+    while (s0 < end) {
+      const char *h = s0;
+      const char *n = s1;
+
+      while (*n && (std::toupper(static_cast<unsigned char>(*h)) == *n)) {
+        ++h;
+        ++n;
+      }
+
+      if (!*n) {
+        return s0;
+      }
+
+      ++s0;
+    }
+
+    return nullptr;
+  }
+
 private:
   char **m_value = nullptr; // the array of values
 
@@ -153,25 +193,21 @@ private:
   // (as strcmp does)
   using OperatorFunction = int (*)(const char *, const char *);
 
+  // return 0 if s1 is substring of s0 and 1 otherwise, to conform to strcmp 
etc.
   static int
   _isSubstring(const char *s0, const char *s1)
   {
-    // return 0 if s1 is substring of s0 and 1 otherwise
-    // this reverse behavior is to conform to the behavior of strcmp
-    // which returns 0 if strings match
     return (strstr(s0, s1) == nullptr ? 1 : 0);
   }
 
-  enum LengthCondition {
-    DATA_LENGTH_EQUAL = 0,
-    DATA_LENGTH_LARGER,
-  };
-
-  inline bool _checkCondition(OperatorFunction f, const char *field_value, 
size_t field_value_length, char **val,
-                              LengthCondition lc);
+  //
+  static int
+  _isSubstringUpper(const char *s0, const char *s1)
+  {
+    return (strstrcase(s0, s1) == nullptr) ? 1 : 0;
+  }
 
-  inline bool _checkConditionAndWipe(OperatorFunction f, char **field_value, 
size_t field_value_length, char **val,
-                                     const char *uppercase_field_value, 
LengthCondition lc);
+  bool _checkConditionAndWipe(OperatorFunction f, char *field_value, size_t 
field_value_length, char **val, bool uppercase = false);
 
   // -- member functions that are not allowed --
   LogFilterString();
@@ -193,7 +229,6 @@ public:
   bool operator==(LogFilterInt &rhs);
 
   bool toss_this_entry(LogAccess *lad) override;
-  bool wipe_this_entry(LogAccess *lad) override;
   void display(FILE *fd = stdout) override;
 
   // noncopyable
@@ -226,7 +261,6 @@ public:
   bool operator==(LogFilterIP &rhs);
 
   bool toss_this_entry(LogAccess *lad) override;
-  bool wipe_this_entry(LogAccess *lad) override;
   void display(FILE *fd = stdout) override;
 
   // noncopyable
@@ -259,7 +293,6 @@ public:
 
   void       add(LogFilter *filter, bool copy = true);
   bool       toss_this_entry(LogAccess *lad);
-  bool       wipe_this_entry(LogAccess *lad);
   LogFilter *find_by_name(const char *name);
   void       clear();
 
@@ -278,18 +311,6 @@ public:
   unsigned count() const;
   void     display(FILE *fd = stdout);
 
-  bool
-  does_conjunction() const
-  {
-    return m_does_conjunction;
-  }
-
-  void
-  set_conjunction(bool c)
-  {
-    m_does_conjunction = c;
-  }
-
   // noncopyable
   // -- member functions that are not allowed --
   LogFilterList(const LogFilterList &rhs)            = delete;
@@ -298,263 +319,5 @@ public:
 private:
   Queue<LogFilter> m_filter_list;
 
-  bool m_does_conjunction = true;
-  // If m_does_conjunction = true
-  // toss_this_entry returns true
-  // if ANY filter tosses entry away.
-  // If m_does_conjunction = false,
-  // toss this entry returns true if
-  // ALL filters toss away entry
+  bool m_has_wipe = false;
 };
-
-/*-------------------------------------------------------------------------
-  Inline functions
-  -------------------------------------------------------------------------*/
-
-/*-------------------------------------------------------------------------
-  _checkCondition
-
-  check all values for a matching condition
-
-  the arguments to the function are:
-
-  - a function f of type OperatorFunction that determines if the
-    condition is true for a single filter value. Note that this function
-    must return 0 if the condition is true.
-  - the value of the field from the log record
-  - the length of this field
-  - the array of filter values to compare to note that we pass this as an
-    argument because it can be either m_value or m_value_uppercase
-  - a LengthCondition argument that determines if the length of the field value
-    must be equal or larger to the length of the filter value (this is to
-    compare strings only if really needed
-    ------------------------------------------------------------------------*/
-
-inline bool
-LogFilterString::_checkCondition(OperatorFunction f, const char *field_value, 
size_t field_value_length, char **val,
-                                 LengthCondition lc)
-{
-  bool retVal = false;
-
-  // make single value case a little bit faster by taking it out of loop
-  //
-  if (m_num_values == 1) {
-    switch (lc) {
-    case DATA_LENGTH_EQUAL:
-      retVal = (field_value_length == *m_length ? ((*f)(field_value, *val) == 
0 ? true : false) : false);
-      break;
-    case DATA_LENGTH_LARGER:
-      retVal = (field_value_length > *m_length ? ((*f)(field_value, *val) == 0 
? true : false) : false);
-      break;
-    default:
-      ink_assert(!"LogFilterString::checkCondition "
-                  "unknown LengthCondition");
-    }
-  } else {
-    size_t i;
-    switch (lc) {
-    case DATA_LENGTH_EQUAL:
-      for (i = 0; i < m_num_values; ++i) {
-        // condition is satisfied if f returns zero
-        if (field_value_length == m_length[i] && (*f)(field_value, val[i]) == 
0) {
-          retVal = true;
-          break;
-        }
-      }
-      break;
-    case DATA_LENGTH_LARGER:
-      for (i = 0; i < m_num_values; ++i) {
-        // condition is satisfied if f returns zero
-        if (field_value_length > m_length[i] && (*f)(field_value, val[i]) == 
0) {
-          retVal = true;
-          break;
-        }
-      }
-      break;
-    default:
-      ink_assert(!"LogFilterString::checkCondition "
-                  "unknown LengthCondition");
-    }
-  }
-  return retVal;
-}
-
-/*---------------------------------------------------------------------------
- * find pattern from the query param
- * 1) if pattern is not in the query param, return nullptr
- * 2) if got the pattern in one param's value, search again until it's in one 
param name, or nullptr if can't find it from param
-name
----------------------------------------------------------------------------*/
-static const char *
-findPatternFromParamName(const char *lookup_query_param, const char *pattern)
-{
-  const char *pattern_in_query_param = strstr(lookup_query_param, pattern);
-  while (pattern_in_query_param) {
-    // wipe pattern in param name, need to search again if find pattern in 
param value
-    const char *param_value_str = strchr(pattern_in_query_param, '=');
-    if (!param_value_str) {
-      // no "=" after pattern_in_query_param, means pattern_in_query_param is 
not in the param name, and no more param after it
-      pattern_in_query_param = nullptr;
-      break;
-    }
-    const char *param_name_str = strchr(pattern_in_query_param, '&');
-    if (param_name_str && param_value_str > param_name_str) {
-      //"=" is after "&" followd by pattern_in_query_param, means 
pattern_in_query_param is not in the param name
-      pattern_in_query_param = strstr(param_name_str, pattern);
-      continue;
-    }
-    // ensure pattern_in_query_param is in the param name now
-    break;
-  }
-  return pattern_in_query_param;
-}
-
-/*---------------------------------------------------------------------------
- * replace param value whose name contains pattern with same count 'X' of 
original value str length
----------------------------------------------------------------------------*/
-static void
-updatePatternForFieldValue(char **field, const char *pattern_str, int /* 
field_pos ATS_UNUSED */, char *buf_dest)
-{
-  int   buf_dest_len = strlen(buf_dest);
-  char  buf_dest_to_field[buf_dest_len + 1];
-  char *temp_text = buf_dest_to_field;
-  memcpy(temp_text, buf_dest, (pattern_str - buf_dest));
-  temp_text             += (pattern_str - buf_dest);
-  const char *value_str  = strchr(pattern_str, '=');
-  if (value_str) {
-    value_str++;
-    memcpy(temp_text, pattern_str, (value_str - pattern_str));
-    temp_text                  += (value_str - pattern_str);
-    const char *next_param_str  = strchr(value_str, '&');
-    if (next_param_str) {
-      for (int i = 0; i < (next_param_str - value_str); i++) {
-        temp_text[i] = 'X';
-      }
-      temp_text += (next_param_str - value_str);
-      memcpy(temp_text, next_param_str, ((buf_dest + buf_dest_len) - 
next_param_str));
-    } else {
-      for (int i = 0; i < ((buf_dest + buf_dest_len) - value_str); i++) {
-        temp_text[i] = 'X';
-      }
-    }
-  } else {
-    return;
-  }
-
-  buf_dest_to_field[buf_dest_len] = '\0';
-  strcpy(*field, buf_dest_to_field);
-}
-
-/*---------------------------------------------------------------------------
-  wipeField : Given a dest buffer, wipe the first occurrence of the value of 
the
-  field in the buffer.
-
---------------------------------------------------------------------------*/
-static void
-wipeField(char **field, char *pattern, const char *uppercase_field)
-{
-  char       *buf_dest    = *field;
-  const char *lookup_dest = uppercase_field ? uppercase_field : *field;
-
-  if (buf_dest) {
-    char       *query_param        = strchr(buf_dest, '?');
-    const char *lookup_query_param = strchr(lookup_dest, '?');
-    if (!query_param || !lookup_query_param) {
-      return;
-    }
-
-    const char *pattern_in_param_name = 
findPatternFromParamName(lookup_query_param, pattern);
-    while (pattern_in_param_name) {
-      int field_pos         = pattern_in_param_name - lookup_query_param;
-      pattern_in_param_name = query_param + field_pos;
-      updatePatternForFieldValue(field, pattern_in_param_name, field_pos, 
buf_dest);
-
-      // search new param again
-      const char *new_param = strchr(lookup_query_param + field_pos, '&');
-      if (new_param && *(new_param + 1)) {
-        pattern_in_param_name = findPatternFromParamName(new_param + 1, 
pattern);
-      } else {
-        break;
-      }
-    }
-  }
-}
-
-/*-------------------------------------------------------------------------
-  _checkConditionAndWipe
-
-  check all values for a matching condition and perform wipe action
-
-  the arguments to the function are:
-
-  - a function f of type OperatorFunction that determines if the
-    condition is true for a single filter value. Note that this function
-    must return 0 if the condition is true.
-  - the value of the field from the log record
-  - the length of this field
-  - the array of filter values to compare to note that we pass this as an
-    argument because it can be either m_value or m_value_uppercase
-  - a LengthCondition argument that determines if the length of the field value
-    must be equal or larger to the length of the filter value (this is to
-    compare strings only if really needed
-    ------------------------------------------------------------------------*/
-
-inline bool
-LogFilterString::_checkConditionAndWipe(OperatorFunction f, char 
**field_value, size_t field_value_length, char **val,
-                                        const char *uppercase_field_value, 
LengthCondition lc)
-{
-  bool retVal = false;
-
-  if (m_action != WIPE_FIELD_VALUE) {
-    return false;
-  }
-
-  // make single value case a little bit faster by taking it out of loop
-  //
-  const char *lookup_field_value = uppercase_field_value ? 
uppercase_field_value : *field_value;
-  if (m_num_values == 1) {
-    switch (lc) {
-    case DATA_LENGTH_EQUAL:
-      retVal = (field_value_length == *m_length ? ((*f)(lookup_field_value, 
*val) == 0 ? true : false) : false);
-      if (retVal) {
-        wipeField(field_value, *val, uppercase_field_value);
-      }
-      break;
-    case DATA_LENGTH_LARGER:
-      retVal = (field_value_length > *m_length ? ((*f)(lookup_field_value, 
*val) == 0 ? true : false) : false);
-      if (retVal) {
-        wipeField(field_value, *val, uppercase_field_value);
-      }
-      break;
-    default:
-      ink_assert(!"LogFilterString::checkCondition "
-                  "unknown LengthCondition");
-    }
-  } else {
-    size_t i;
-    switch (lc) {
-    case DATA_LENGTH_EQUAL:
-      for (i = 0; i < m_num_values; ++i) {
-        // condition is satisfied if f returns zero
-        if (field_value_length == m_length[i] && (*f)(lookup_field_value, 
val[i]) == 0) {
-          retVal = true;
-          wipeField(field_value, val[i], uppercase_field_value);
-        }
-      }
-      break;
-    case DATA_LENGTH_LARGER:
-      for (i = 0; i < m_num_values; ++i) {
-        // condition is satisfied if f returns zero
-        if (field_value_length > m_length[i] && (*f)(lookup_field_value, 
val[i]) == 0) {
-          retVal = true;
-          wipeField(field_value, val[i], uppercase_field_value);
-        }
-      }
-      break;
-    default:
-      ink_assert(!"LogFilterString::checkConditionAndWipe "
-                  "unknown LengthConditionAndWipe");
-    }
-  }
-  return retVal;
-}
diff --git a/src/proxy/logging/LogField.cc b/src/proxy/logging/LogField.cc
index 0373ec6dee..f7dcbbd067 100644
--- a/src/proxy/logging/LogField.cc
+++ b/src/proxy/logging/LogField.cc
@@ -367,6 +367,7 @@ LogField::LogField(const char *field, Container container)
 
   case ICFG:
     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
+    m_type           = LogField::sINT;
     break;
 
   case RECORD:
@@ -379,6 +380,7 @@ LogField::LogField(const char *field, Container container)
       Note("Invalid milestone name in LogField ctor: %s", m_name);
     }
     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
+    m_type           = LogField::sINT;
     break;
 
   case MSDMS: {
@@ -387,6 +389,7 @@ LogField::LogField(const char *field, Container container)
       Note("Invalid milestone range in LogField ctor: %s", m_name);
     }
     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
+    m_type           = LogField::sINT;
     break;
   }
 
@@ -406,8 +409,8 @@ LogField::LogField(const LogField &rhs)
     m_agg_op(rhs.m_agg_op),
     m_agg_cnt(0),
     m_agg_val(0),
-    m_milestone1(TS_MILESTONE_LAST_ENTRY),
-    m_milestone2(TS_MILESTONE_LAST_ENTRY),
+    m_milestone1(rhs.m_milestone1),
+    m_milestone2(rhs.m_milestone2),
     m_time_field(rhs.m_time_field),
     m_alias_map(rhs.m_alias_map),
     m_set_func(rhs.m_set_func)
@@ -514,7 +517,7 @@ LogField::updateField(LogAccess *lad, char *buf, int len)
 /*-------------------------------------------------------------------------
   LogField::marshal
 
-  This routine will marshsal the given field into the buffer provided.
+  This routine will marshal the given field into the buffer provided.
   -------------------------------------------------------------------------*/
 unsigned
 LogField::marshal(LogAccess *lad, char *buf)
diff --git a/src/proxy/logging/LogFilter.cc b/src/proxy/logging/LogFilter.cc
index 8be0b3ae03..fcfe41c4e3 100644
--- a/src/proxy/logging/LogFilter.cc
+++ b/src/proxy/logging/LogFilter.cc
@@ -46,8 +46,9 @@
 #include "proxy/logging/LogConfig.h"
 #include "proxy/logging/Log.h"
 
-const char *LogFilter::OPERATOR_NAME[] = {"MATCH", "CASE_INSENSITIVE_MATCH", 
"CONTAIN", "CASE_INSENSITIVE_CONTAIN"};
-const char *LogFilter::ACTION_NAME[]   = {"REJECT", "ACCEPT", 
"WIPE_FIELD_VALUE"};
+const char *LogFilter::OPERATOR_NAME[] = {
+  "MATCH", "CASE_INSENSITIVE_MATCH", "CONTAIN", "CASE_INSENSITIVE_CONTAIN", 
"LT", "LTE", "GT", "GTE"};
+const char *LogFilter::ACTION_NAME[] = {"REJECT", "ACCEPT", 
"WIPE_FIELD_VALUE"};
 
 namespace
 {
@@ -226,6 +227,160 @@ LogFilterString::_setValues(size_t n, char **value)
   }
 }
 
+/*---------------------------------------------------------------------------
+ * find pattern from the query param
+ * 1) if pattern is not in the query param, return nullptr
+ * 2) if got the pattern in one param's value, search again until it's in one 
param name, or nullptr if can't find it from param
+name
+---------------------------------------------------------------------------*/
+static const char *
+findPatternFromParamName(const char *lookup_query_param, const char *pattern, 
bool uppercase)
+{
+  const char *pattern_in_query_param =
+    uppercase ? LogFilterString::strstrcase(lookup_query_param, pattern) : 
strstr(lookup_query_param, pattern);
+
+  while (pattern_in_query_param) {
+    // wipe pattern in param name, need to search again if find pattern in 
param value
+    const char *param_value_str = strchr(pattern_in_query_param, '=');
+
+    if (!param_value_str) {
+      // no "=" after pattern_in_query_param, means pattern_in_query_param is 
not in the param name, and no more param after it
+      pattern_in_query_param = nullptr;
+      break;
+    }
+
+    const char *param_name_str = strchr(pattern_in_query_param, '&');
+
+    if (param_name_str && param_value_str > param_name_str) {
+      //"=" is after "&" followd by pattern_in_query_param, means 
pattern_in_query_param is not in the param name
+      if (uppercase) {
+        pattern_in_query_param = LogFilterString::strstrcase(param_name_str, 
pattern);
+      } else {
+        pattern_in_query_param = strstr(param_name_str, pattern);
+      }
+      continue;
+    }
+    // ensure pattern_in_query_param is in the param name now
+    break;
+  }
+
+  return pattern_in_query_param;
+}
+
+/*---------------------------------------------------------------------------
+ * replace param value whose name contains pattern with same count 'X' of 
original value str length
+---------------------------------------------------------------------------*/
+static void
+updatePatternForFieldValue(char *field, const char *pattern_str, int /* 
field_pos ATS_UNUSED */, char *buf_dest)
+{
+  int   buf_dest_len = strlen(buf_dest);
+  char  buf_dest_to_field[buf_dest_len + 1];
+  char *temp_text = buf_dest_to_field;
+
+  memcpy(temp_text, buf_dest, (pattern_str - buf_dest));
+  temp_text += (pattern_str - buf_dest);
+
+  const char *value_str = strchr(pattern_str, '=');
+
+  if (value_str) {
+    value_str++;
+    memcpy(temp_text, pattern_str, (value_str - pattern_str));
+    temp_text += (value_str - pattern_str);
+
+    const char *next_param_str = strchr(value_str, '&');
+
+    if (next_param_str) {
+      for (int i = 0; i < (next_param_str - value_str); i++) {
+        temp_text[i] = 'X';
+      }
+      temp_text += (next_param_str - value_str);
+      memcpy(temp_text, next_param_str, ((buf_dest + buf_dest_len) - 
next_param_str));
+    } else {
+      for (int i = 0; i < ((buf_dest + buf_dest_len) - value_str); i++) {
+        temp_text[i] = 'X';
+      }
+    }
+  } else {
+    return;
+  }
+
+  buf_dest_to_field[buf_dest_len] = '\0';
+  strcpy(field, buf_dest_to_field);
+}
+
+/*---------------------------------------------------------------------------
+  wipeField : Given a dest buffer, wipe the first occurrence of the value of 
the
+  field in the buffer.
+
+--------------------------------------------------------------------------*/
+static void
+wipeField(char *field, char *pattern, bool uppercase)
+{
+  if (field) {
+    char *query_param = strchr(field, '?');
+
+    if (!query_param) {
+      return;
+    }
+
+    const char *pattern_in_param_name = findPatternFromParamName(query_param, 
pattern, uppercase);
+
+    while (pattern_in_param_name) {
+      int field_pos = pattern_in_param_name - query_param;
+
+      pattern_in_param_name = query_param + field_pos;
+      updatePatternForFieldValue(field, pattern_in_param_name, field_pos, 
field);
+
+      // search new param again
+      const char *new_param = strchr(query_param + field_pos, '&');
+
+      if (new_param && *(new_param + 1)) {
+        pattern_in_param_name = findPatternFromParamName(new_param + 1, 
pattern, uppercase);
+      } else {
+        break;
+      }
+    }
+  }
+}
+
+/*-------------------------------------------------------------------------
+  _checkConditionAndWipe
+
+  check all values for a matching condition and perform wipe action if
+  appropriate. This replaces both of the checkConditions functions.
+
+  the arguments to the function are:
+
+  - a function f of type OperatorFunction that determines if the
+    condition is true for a single filter value. Note that this function
+    must return 0 if the condition is true.
+  - the value of the field from the log record
+  - the length of this field
+  - the array of filter values to compare to note that we pass this as an
+    argument because it can be either m_value or m_value_uppercase
+    ------------------------------------------------------------------------*/
+inline bool
+LogFilterString::_checkConditionAndWipe(OperatorFunction f, char *field_value, 
size_t field_value_length, char **val,
+                                        bool uppercase)
+{
+  bool retVal = false;
+
+  for (size_t i = 0; i < m_num_values; ++i) {
+    // condition is satisfied if f returns zero
+    if (field_value_length > m_length[i] && (*f)(field_value, val[i]) == 0) {
+      retVal = true;
+      if (m_action == WIPE_FIELD_VALUE) {
+        Dbg(dbg_ctl_log, "entry wiped, ...");
+        wipeField(field_value, val[i], uppercase);
+      } else {
+        break; // We only break here if this is not a wipe field, since we can 
wipe multiple values
+      }
+    }
+  }
+
+  return retVal;
+}
+
 LogFilterString::LogFilterString(const char *name, LogField *field, 
LogFilter::Action action, LogFilter::Operator oper,
                                  char *values)
   : LogFilter(name, field, action, oper)
@@ -306,89 +461,6 @@ LogFilterString::operator==(LogFilterString &rhs)
   return false;
 }
 
-/*-------------------------------------------------------------------------
-  LogFilterString::wipe_this_entry
-
-  For strings, we need to marshal the given string into a buffer so that we
-  can compare it with the filter value.  Most strings are snall, so we'll
-  only allocate space dynamically if the marshal_len is very large (eg,
-  URL).
-
-  The m_substr field tells us whether we can match based on substrings, or
-  whether we should compare the entire string.
-  -------------------------------------------------------------------------*/
-
-bool
-LogFilterString::wipe_this_entry(LogAccess *lad)
-{
-  if (m_num_values == 0 || m_field == nullptr || lad == nullptr || m_action != 
WIPE_FIELD_VALUE) {
-    return false;
-  }
-
-  static const unsigned BUFSIZE = 1024;
-  char                  small_buf[BUFSIZE];
-  char                  small_buf_upper[BUFSIZE];
-  char                 *big_buf       = nullptr;
-  char                 *big_buf_upper = nullptr;
-  char                 *buf           = small_buf;
-  char                 *buf_upper     = small_buf_upper;
-  size_t                marsh_len     = m_field->marshal_len(lad); // includes 
null termination
-
-  if (marsh_len > BUFSIZE) {
-    big_buf = static_cast<char *>(ats_malloc(marsh_len));
-    ink_assert(big_buf != nullptr);
-    buf = big_buf;
-  }
-
-  ink_assert(buf != nullptr);
-  m_field->marshal(lad, buf);
-
-  ink_assert(buf != nullptr);
-
-  bool cond_satisfied = false;
-  switch (m_operator) {
-  case MATCH:
-    // marsh_len is an upper bound on the length of the marshalled string
-    // because marsh_len counts padding and the eos. So for a MATCH
-    // operator, we use the DATA_LENGTH_LARGER length condition rather
-    // than DATA_LENGTH_EQUAL, which we would use if we had the actual
-    // length of the string. It is probably not worth computing the
-    // actual length, so we just use the fact that a MATCH is not possible
-    // when marsh_len <= (length of the filter string)
-    //
-    cond_satisfied = _checkConditionAndWipe(&strcmp, &buf, marsh_len, m_value, 
nullptr, DATA_LENGTH_LARGER);
-    break;
-  case CASE_INSENSITIVE_MATCH:
-    cond_satisfied = _checkConditionAndWipe(&strcasecmp, &buf, marsh_len, 
m_value, nullptr, DATA_LENGTH_LARGER);
-    break;
-  case CONTAIN:
-    cond_satisfied = _checkConditionAndWipe(&_isSubstring, &buf, marsh_len, 
m_value, nullptr, DATA_LENGTH_LARGER);
-    break;
-  case CASE_INSENSITIVE_CONTAIN:
-    if (big_buf) {
-      big_buf_upper = static_cast<char *>(ats_malloc(static_cast<unsigned 
int>(marsh_len)));
-      buf_upper     = big_buf_upper;
-    } else {
-      buf = small_buf; // make clang happy
-    }
-    for (size_t i = 0; i < marsh_len; i++) {
-      buf_upper[i] = ParseRules::ink_toupper(buf[i]);
-    }
-    cond_satisfied = _checkConditionAndWipe(&_isSubstring, &buf, marsh_len, 
m_value_uppercase, buf_upper, DATA_LENGTH_LARGER);
-    break;
-  default:
-    ink_assert(!"INVALID FILTER OPERATOR");
-  }
-
-  if (cond_satisfied) {
-    m_field->updateField(lad, buf, strlen(buf));
-  }
-
-  ats_free(big_buf);
-  ats_free(big_buf_upper);
-  return cond_satisfied;
-}
-
 /*-------------------------------------------------------------------------
   LogFilterString::toss_this_entry
 
@@ -404,65 +476,51 @@ LogFilterString::wipe_this_entry(LogAccess *lad)
 bool
 LogFilterString::toss_this_entry(LogAccess *lad)
 {
+  static const unsigned BUFSIZE = 8192;
+
   if (m_num_values == 0 || m_field == nullptr || lad == nullptr) {
     return false;
   }
 
-  static const unsigned BUFSIZE = 1024;
-  char                  small_buf[BUFSIZE];
-  char                  small_buf_upper[BUFSIZE];
-  char                 *big_buf       = nullptr;
-  char                 *big_buf_upper = nullptr;
-  char                 *buf           = small_buf;
-  char                 *buf_upper     = small_buf_upper;
-  size_t                marsh_len     = m_field->marshal_len(lad); // includes 
null termination
+  char   small_buf[BUFSIZE];
+  char  *big_buf        = nullptr;
+  char  *buf            = small_buf;
+  size_t marsh_len      = m_field->marshal_len(lad); // includes null 
termination
+  bool   cond_satisfied = false;
 
   if (marsh_len > BUFSIZE) {
     big_buf = static_cast<char *>(ats_malloc(static_cast<unsigned 
int>(marsh_len)));
     ink_assert(big_buf != nullptr);
     buf = big_buf;
   }
-
   m_field->marshal(lad, buf);
 
-  bool cond_satisfied = false;
   switch (m_operator) {
   case MATCH:
     // marsh_len is an upper bound on the length of the marshalled string
-    // because marsh_len counts padding and the eos. So for a MATCH
-    // operator, we use the DATA_LENGTH_LARGER length condition rather
-    // than DATA_LENGTH_EQUAL, which we would use if we had the actual
-    // length of the string. It is probably not worth computing the
-    // actual length, so we just use the fact that a MATCH is not possible
-    // when marsh_len <= (length of the filter string)
-    //
-    cond_satisfied = _checkCondition(&strcmp, buf, marsh_len, m_value, 
DATA_LENGTH_LARGER);
+    // because marsh_len counts padding and the eos.
+    cond_satisfied = _checkConditionAndWipe(&strcmp, buf, marsh_len, m_value);
     break;
   case CASE_INSENSITIVE_MATCH:
-    cond_satisfied = _checkCondition(&strcasecmp, buf, marsh_len, m_value, 
DATA_LENGTH_LARGER);
+    cond_satisfied = _checkConditionAndWipe(&strcasecmp, buf, marsh_len, 
m_value);
     break;
   case CONTAIN:
-    cond_satisfied = _checkCondition(&_isSubstring, buf, marsh_len, m_value, 
DATA_LENGTH_LARGER);
+    cond_satisfied = _checkConditionAndWipe(&_isSubstring, buf, marsh_len, 
m_value);
     break;
-  case CASE_INSENSITIVE_CONTAIN: {
-    if (big_buf) {
-      big_buf_upper = static_cast<char *>(ats_malloc(static_cast<unsigned 
int>(marsh_len)));
-      buf_upper     = big_buf_upper;
-    } else {
-      buf = small_buf; // make clang happy
-    }
-    for (size_t i = 0; i < marsh_len; i++) {
-      buf_upper[i] = ParseRules::ink_toupper(buf[i]);
-    }
-    cond_satisfied = _checkCondition(&_isSubstring, buf_upper, marsh_len, 
m_value_uppercase, DATA_LENGTH_LARGER);
+  case CASE_INSENSITIVE_CONTAIN:
+    // This one is special, since we use our strstrcase() function, which 
requires uppercase match values
+    cond_satisfied = _checkConditionAndWipe(&_isSubstringUpper, buf, 
marsh_len, m_value_uppercase, true);
     break;
-  }
   default:
     ink_assert(!"INVALID FILTER OPERATOR");
   }
 
+  // Check if we should call the updateField function for this field
+  if (m_action == WIPE_FIELD_VALUE && cond_satisfied) {
+    m_field->updateField(lad, buf, strlen(buf));
+  }
+
   ats_free(big_buf);
-  ats_free(big_buf_upper);
 
   return ((m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && 
!cond_satisfied));
 }
@@ -502,7 +560,6 @@ LogFilterInt::_setValues(size_t n, int64_t *value)
   }
 }
 
-// TODO: ival should be int64_t
 int
 LogFilterInt::_convertStringToInt(char *value, int64_t *ival, LogFieldAliasMap 
*map)
 {
@@ -557,8 +614,14 @@ LogFilterInt::LogFilterInt(const char *name, LogField 
*field, LogFilter::Action
                                            // pointer in @c field.
 
   if (n) {
+    if (m_operator >= Operator::LT && m_operator <= Operator::GTE && n > 1) {
+      Fatal("Operator %s can only have one value", OPERATOR_NAME[m_operator]);
+      return;
+    }
+
     val_array = new int64_t[n];
     char *t;
+
     while (t = tok.getNext(), t != nullptr) {
       int64_t ival;
       if (!_convertStringToInt(t, &ival, field_map.get())) {
@@ -624,44 +687,6 @@ LogFilterInt::operator==(LogFilterInt &rhs)
   return false;
 }
 
-/*-------------------------------------------------------------------------
-  LogFilterInt::wipe_this_entry
-  -------------------------------------------------------------------------*/
-
-bool
-LogFilterInt::wipe_this_entry(LogAccess *lad)
-{
-  if (m_num_values == 0 || m_field == nullptr || lad == nullptr || m_action != 
WIPE_FIELD_VALUE) {
-    return false;
-  }
-
-  bool    cond_satisfied = false;
-  int64_t value;
-
-  m_field->marshal(lad, reinterpret_cast<char *>(&value));
-  // This used to do an ntohl() on value, but that breaks various filters.
-  // Long term we should move IPs to their own log type.
-
-  // we don't use m_operator because we consider all operators to be
-  // equivalent to "MATCH" for an integer field
-  //
-
-  // most common case is single value, speed it up a little bit by unrolling
-  //
-  if (m_num_values == 1) {
-    cond_satisfied = (value == *m_value);
-  } else {
-    for (size_t i = 0; i < m_num_values; ++i) {
-      if (value == m_value[i]) {
-        cond_satisfied = true;
-        break;
-      }
-    }
-  }
-
-  return cond_satisfied;
-}
-
 /*-------------------------------------------------------------------------
   LogFilterInt::toss_this_entry
   -------------------------------------------------------------------------*/
@@ -677,24 +702,39 @@ LogFilterInt::toss_this_entry(LogAccess *lad)
   int64_t value;
 
   m_field->marshal(lad, reinterpret_cast<char *>(&value));
-  // This used to do an ntohl() on value, but that breaks various filters.
-  // Long term we should move IPs to their own log type.
-
-  // we don't use m_operator because we consider all operators to be
-  // equivalent to "MATCH" for an integer field
-  //
-
-  // most common case is single value, speed it up a little bit by unrolling
-  //
-  if (m_num_values == 1) {
-    cond_satisfied = (value == *m_value);
-  } else {
-    for (size_t i = 0; i < m_num_values; ++i) {
-      if (value == m_value[i]) {
-        cond_satisfied = true;
-        break;
+  switch (m_operator) {
+  case MATCH:
+    // most common case is single value, speed it up a little bit by unrolling
+    //
+    if (m_num_values == 1) {
+      cond_satisfied = (value == *m_value);
+    } else {
+      for (size_t i = 0; i < m_num_values; ++i) {
+        if (value == m_value[i]) {
+          cond_satisfied = true;
+          break;
+        }
       }
     }
+    break;
+  case LT:
+    ink_assert(m_num_values == 1);
+    cond_satisfied = (value < *m_value);
+    break;
+  case LTE:
+    ink_assert(m_num_values == 1);
+    cond_satisfied = (value <= *m_value);
+    break;
+  case GT:
+    ink_assert(m_num_values == 1);
+    cond_satisfied = (value > *m_value);
+    break;
+  case GTE:
+    ink_assert(m_num_values == 1);
+    cond_satisfied = (value >= *m_value);
+    break;
+  default:
+    ink_assert(!"INVALID FILTER OPERATOR");
   }
 
   return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && 
!cond_satisfied);
@@ -852,12 +892,6 @@ LogFilterIP::toss_this_entry(LogAccess *lad)
   return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && 
!cond_satisfied);
 }
 
-bool
-LogFilterIP::wipe_this_entry(LogAccess *)
-{
-  return false;
-}
-
 /*-------------------------------------------------------------------------
   LogFilterIP::display
   -------------------------------------------------------------------------*/
@@ -930,24 +964,20 @@ LogFilterList::~LogFilterList()
 bool
 LogFilterList::operator==(LogFilterList &rhs)
 {
-  if (m_does_conjunction == rhs.does_conjunction()) {
-    LogFilter *f    = first();
-    LogFilter *rhsf = rhs.first();
-
-    while (true) {
-      if (!(f || rhsf)) {
-        return true;
-      } else if (!f || !rhsf) {
-        return false;
-      } else if (!filters_are_equal(f, rhsf)) {
-        return false;
-      } else {
-        f    = next(f);
-        rhsf = rhs.next(rhsf);
-      }
+  LogFilter *f    = first();
+  LogFilter *rhsf = rhs.first();
+
+  while (true) {
+    if (!(f || rhsf)) {
+      return true;
+    } else if (!f || !rhsf) {
+      return false;
+    } else if (!filters_are_equal(f, rhsf)) {
+      return false;
+    } else {
+      f    = next(f);
+      rhsf = rhs.next(rhsf);
     }
-  } else {
-    return false;
   }
 }
 
@@ -984,21 +1014,8 @@ LogFilterList::add(LogFilter *filter, bool copy)
   } else {
     m_filter_list.enqueue(filter);
   }
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
 
-bool
-LogFilterList::wipe_this_entry(LogAccess *lad)
-{
-  bool wipeFlag = false;
-  for (LogFilter *f = first(); f; f = next(f)) {
-    if (f->wipe_this_entry(lad)) {
-      wipeFlag = true;
-    }
-  }
-  return wipeFlag;
+  m_has_wipe |= filter->is_wipe();
 }
 
 /*-------------------------------------------------------------------------
@@ -1007,25 +1024,18 @@ LogFilterList::wipe_this_entry(LogAccess *lad)
 bool
 LogFilterList::toss_this_entry(LogAccess *lad)
 {
-  if (m_does_conjunction) {
-    // toss if any filter rejects the entry (all filters should accept)
-    //
-    for (LogFilter *f = first(); f; f = next(f)) {
-      if (f->toss_this_entry(lad)) {
+  bool retval = false;
+
+  for (LogFilter *f = first(); f; f = next(f)) {
+    if (f->toss_this_entry(lad)) {
+      if (m_has_wipe) { // Continue filters evaluation if there is a wipe 
filter in the list
+        retval = true;
+      } else {
         return true;
       }
     }
-    return false;
-  } else {
-    // toss if all filters reject the entry (any filter accepts)
-    //
-    for (LogFilter *f = first(); f; f = next(f)) {
-      if (!f->toss_this_entry(lad)) {
-        return false;
-      }
-    }
-    return true;
   }
+  return retval;
 }
 
 /*-------------------------------------------------------------------------
diff --git a/src/proxy/logging/LogObject.cc b/src/proxy/logging/LogObject.cc
index 1b260d49f5..d87def246c 100644
--- a/src/proxy/logging/LogObject.cc
+++ b/src/proxy/logging/LogObject.cc
@@ -603,10 +603,6 @@ LogObject::log(LogAccess *lad, std::string_view text_entry)
     return Log::SKIP;
   }
 
-  if (lad && m_filter_list.wipe_this_entry(lad)) {
-    Dbg(dbg_ctl_log, "entry wiped, ...");
-  }
-
   if (lad && m_format->is_aggregate()) {
     // marshal the field data into the temp space provided by the
     // LogFormat object for aggregate formats


Reply via email to