pw_log: Add compile-time log filtering

This adds compile-time log filtering to the facade, controlled by
macros. By default there is a simple PW_LOG_LEVEL macro that controls
logs by level; and a more involved PW_LOG_ENABLE_IF(level, flags) macro
that can do more sophisticated logic if needed.

Change-Id: I3613ffe3f7cd34c6da5cc17e592725c251bdd775
diff --git a/pw_log/basic_log_test.cc b/pw_log/basic_log_test.cc
index 0c0a92e..1b640c4 100644
--- a/pw_log/basic_log_test.cc
+++ b/pw_log/basic_log_test.cc
@@ -22,6 +22,7 @@
 #define PW_LOG_MODULE_NAME "TST"
 #define PW_LOG_USE_SHORT_NAMES 1
 #define PW_LOG_USE_ULTRA_SHORT_NAMES 1
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
 #include "pw_log/log.h"
 // clang-format on
 
diff --git a/pw_log/basic_log_test_plain_c.c b/pw_log/basic_log_test_plain_c.c
index b88d5cd..ead81f3 100644
--- a/pw_log/basic_log_test_plain_c.c
+++ b/pw_log/basic_log_test_plain_c.c
@@ -85,11 +85,11 @@
 
   // Log levels other than the standard ones work; what each backend does is
   // implementation defined.
-  PW_LOG(0, PW_LOG_NO_FLAGS, "Custom log level: 0");
-  PW_LOG(1, PW_LOG_NO_FLAGS, "Custom log level: 1");
-  PW_LOG(2, PW_LOG_NO_FLAGS, "Custom log level: 2");
-  PW_LOG(3, PW_LOG_NO_FLAGS, "Custom log level: 3");
-  PW_LOG(100, PW_LOG_NO_FLAGS, "Custom log level: 100");
+  PW_LOG(0, PW_LOG_DEFAULT_FLAGS, "Custom log level: 0");
+  PW_LOG(1, PW_LOG_DEFAULT_FLAGS, "Custom log level: 1");
+  PW_LOG(2, PW_LOG_DEFAULT_FLAGS, "Custom log level: 2");
+  PW_LOG(3, PW_LOG_DEFAULT_FLAGS, "Custom log level: 3");
+  PW_LOG(100, PW_LOG_DEFAULT_FLAGS, "Custom log level: 100");
 
   // Logging from a function.
   LoggingFromFunctionPlainC();
diff --git a/pw_log/docs.rst b/pw_log/docs.rst
index be5d74a..120333d 100644
--- a/pw_log/docs.rst
+++ b/pw_log/docs.rst
@@ -120,7 +120,7 @@
 
   .. code-block:: cpp
 
-    PW_LOG(PW_NO_FLAGS, PW_LOG_LEVEL_INFO, "Temperature is %d degrees", temp);
+    PW_LOG(PW_LOG_DEFAULT_FLAGS, PW_LOG_LEVEL_INFO, "Temp is %d degrees", temp);
     PW_LOG(UNRELIABLE_DELIVERY, PW_LOG_LEVEL_ERROR, "It didn't work!");
 
   .. note::
@@ -138,7 +138,69 @@
 .. cpp:function:: PW_LOG_ERROR(fmt, ...)
 .. cpp:function:: PW_LOG_CRITICAL(fmt, ...)
 
-  Shorthand for `PW_LOG(PW_LOG_NO_FLAGS, <level>, fmt, ...)`.
+  Shorthand for `PW_LOG(PW_LOG_DEFAULT_FLAGS, <level>, fmt, ...)`.
+
+Filtering logs
+--------------
+
+``pw_log`` supports compile time filtering of logs through two mechanisms.
+
+1. Filter by level. Source files that define ``PW_LOG_LEVEL`` will display all
+   logs at or above the chosen level.
+
+   Example:
+
+   .. code-block:: cpp
+
+     #include "pw_log/log.h"
+
+     #define PW_LOG_LEVEL PW_LOG_LEVEL_INFO
+
+     void DoSomething() {
+       PW_LOG_DEBUG("This won't be logged at all");
+       PW_LOG_INFO("This is INFO level, and will display");
+       PW_LOG_WARN("This is above INFO level, and will display");
+     }
+
+2. Filter by arbitrary expression based on ``level`` and ``flags``. Source
+   files that define ``PW_LOG_ENABLE_IF(level, flags)`` will display if the
+   given expression returns true.
+
+   Example:
+
+   .. code-block:: cpp
+
+     #include "pw_log/log.h"
+
+     // This define might be supplied by the build system.
+     #define MY_PRODUCT_LOG_PII_ENABLED false
+
+     // This is the PII mask bit selected by the application.
+     #define MY_PRODUCT_PII_MASK (1 << 5)
+
+     // Pigweed's log facade will call this macro to decide to log or not. In
+     // this case, it will drop logs with the PII flag set if display of PII is
+     // not enabled for the application.
+     #define PW_LOG_ENABLE_IF(level, flags) \
+         (level >= PW_LOG_INFO && \
+          !((flags & MY_PRODUCT_PII_MASK) && MY_PRODUCT_LOG_PII_ENABLED)
+
+     void DoSomethingWithSensitiveInfo() {
+       PW_LOG_DEBUG("This won't be logged at all");
+       PW_LOG_INFO("This is INFO level, and will display");
+
+       // In this example, this will not be logged since logging with PII
+       // is disabled by the above macros.
+       PW_LOG(PW_LOG_LEVEL_INFO,
+              MY_PRODUCT_PII_MASK,
+              "Sensitive: %d",
+              sensitive_info);
+     }
+
+.. attention::
+
+  At this time, only compile time filtering is supported. In the future, we
+  plan to add support for runtime filtering.
 
 Logging attributes
 ------------------
diff --git a/pw_log/public/pw_log/log.h b/pw_log/public/pw_log/log.h
index 66203a8..1eaab54 100644
--- a/pw_log/public/pw_log/log.h
+++ b/pw_log/public/pw_log/log.h
@@ -64,38 +64,65 @@
 #endif  // PW_LOG_MODULE_NAME
 
 // Default: Flags
-#ifndef PW_LOG_NO_FLAGS
-#define PW_LOG_NO_FLAGS 0
-#endif  // PW_LOG_NO_FLAGS
+// For log statements like LOG_INFO that don't have an explicit argument, this
+// is used for the flags value.
+#ifndef PW_LOG_DEFAULT_FLAGS
+#define PW_LOG_DEFAULT_FLAGS 0
+#endif  // PW_LOG_DEFAULT_FLAGS
 
-// Note: The filtering semantics are likely to change.
+// Default: Log level filtering
+//
+// All log statements have a level, and this define is the default filtering.
+// This is compile-time filtering if the level is a constant.
+//
+// TODO(pwbug/17): Convert this to the config system when available.
+#ifndef PW_LOG_LEVEL
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+#endif  // PW_LOG_LEVEL
+
+// Default: Log enabled expression
+//
+// This expression determines whether or not the statement is enabled and
+// should be passed to the backend.
+#ifndef PW_LOG_ENABLE_IF
+#define PW_LOG_ENABLE_IF(level, flags) ((level) >= PW_LOG_LEVEL)
+#endif  // PW_LOG_ENABLE_IF
+
+#ifndef PW_LOG
+#define PW_LOG(level, flags, message, ...)               \
+  do {                                                   \
+    if (PW_LOG_ENABLE_IF(level, flags)) {                \
+      PW_HANDLE_LOG(level, flags, message, __VA_ARGS__); \
+    }                                                    \
+  } while (0)
+#endif  // PW_LOG_DEBUG
 
 // For backends that elect to only provide the general PW_LOG() macro and not
 // specialized versions, define the standard PW_LOG_<level>() macros in terms
 // of the general PW_LOG().
 #ifndef PW_LOG_DEBUG
 #define PW_LOG_DEBUG(message, ...) \
-  PW_LOG(PW_LOG_LEVEL_DEBUG, PW_LOG_NO_FLAGS, message, __VA_ARGS__)
+  PW_LOG(PW_LOG_LEVEL_DEBUG, PW_LOG_DEFAULT_FLAGS, message, __VA_ARGS__)
 #endif  // PW_LOG_DEBUG
 
 #ifndef PW_LOG_INFO
 #define PW_LOG_INFO(message, ...) \
-  PW_LOG(PW_LOG_LEVEL_INFO, PW_LOG_NO_FLAGS, message, __VA_ARGS__)
+  PW_LOG(PW_LOG_LEVEL_INFO, PW_LOG_DEFAULT_FLAGS, message, __VA_ARGS__)
 #endif  // PW_LOG_INFO
 
 #ifndef PW_LOG_WARN
 #define PW_LOG_WARN(message, ...) \
-  PW_LOG(PW_LOG_LEVEL_WARN, PW_LOG_NO_FLAGS, message, __VA_ARGS__)
+  PW_LOG(PW_LOG_LEVEL_WARN, PW_LOG_DEFAULT_FLAGS, message, __VA_ARGS__)
 #endif  // PW_LOG_WARN
 
 #ifndef PW_LOG_ERROR
 #define PW_LOG_ERROR(message, ...) \
-  PW_LOG(PW_LOG_LEVEL_ERROR, PW_LOG_NO_FLAGS, message, __VA_ARGS__)
+  PW_LOG(PW_LOG_LEVEL_ERROR, PW_LOG_DEFAULT_FLAGS, message, __VA_ARGS__)
 #endif  // PW_LOG_ERROR
 
 #ifndef PW_LOG_CRITICAL
 #define PW_LOG_CRITICAL(message, ...) \
-  PW_LOG(PW_LOG_LEVEL_CRITICAL, PW_LOG_NO_FLAGS, message, __VA_ARGS__)
+  PW_LOG(PW_LOG_LEVEL_CRITICAL, PW_LOG_DEFAULT_FLAGS, message, __VA_ARGS__)
 #endif  // PW_LOG_CRITICAL
 
 // Define short, usable names if requested.
diff --git a/pw_log_basic/public/pw_log_basic/log_basic.h b/pw_log_basic/public/pw_log_basic/log_basic.h
index 305b786..6ebb4e1 100644
--- a/pw_log_basic/public/pw_log_basic/log_basic.h
+++ b/pw_log_basic/public/pw_log_basic/log_basic.h
@@ -39,13 +39,13 @@
 // char[] variable inside functions with a log.
 //
 // TODO(pwbug/87): Reconsider the naming of this module when more is in place.
-#define PW_LOG(level, flags, message, ...)      \
-  do {                                          \
-    pw_Log((level),                             \
-           (flags),                             \
-           PW_LOG_MODULE_NAME,                  \
-           __FILE__,                            \
-           __LINE__,                            \
-           __func__,                            \
-           message PW_COMMA_ARGS(__VA_ARGS__)); \
+#define PW_HANDLE_LOG(level, flags, message, ...) \
+  do {                                            \
+    pw_Log((level),                               \
+           (flags),                               \
+           PW_LOG_MODULE_NAME,                    \
+           __FILE__,                              \
+           __LINE__,                              \
+           __func__,                              \
+           message PW_COMMA_ARGS(__VA_ARGS__));   \
   } while (0)
diff --git a/pw_log_tokenized/public_overrides/pw_log_backend/log_backend.h b/pw_log_tokenized/public_overrides/pw_log_backend/log_backend.h
index 86f1c2a..b6035d9 100644
--- a/pw_log_tokenized/public_overrides/pw_log_backend/log_backend.h
+++ b/pw_log_tokenized/public_overrides/pw_log_backend/log_backend.h
@@ -18,4 +18,4 @@
 
 #include "pw_log_tokenized/log_tokenized.h"
 
-#define PW_LOG PW_LOG_TOKENIZED_TO_GLOBAL_HANDLER_WITH_PAYLOAD
+#define PW_HANDLE_LOG PW_LOG_TOKENIZED_TO_GLOBAL_HANDLER_WITH_PAYLOAD
diff --git a/pw_unit_test/logging_event_handler.cc b/pw_unit_test/logging_event_handler.cc
index bd063a2..e1ad080 100644
--- a/pw_unit_test/logging_event_handler.cc
+++ b/pw_unit_test/logging_event_handler.cc
@@ -64,14 +64,17 @@
   const char* result = expectation.success ? "Success" : "Failure";
   uint32_t level = expectation.success ? PW_LOG_LEVEL_INFO : PW_LOG_LEVEL_ERROR;
   PW_LOG(level,
-         PW_LOG_NO_FLAGS,
+         PW_LOG_DEFAULT_FLAGS,
          "%s:%d: %s",
          test_case.file_name,
          expectation.line_number,
          result);
-  PW_LOG(level, PW_LOG_NO_FLAGS, "      Expected: %s", expectation.expression);
   PW_LOG(level,
-         PW_LOG_NO_FLAGS,
+         PW_LOG_DEFAULT_FLAGS,
+         "      Expected: %s",
+         expectation.expression);
+  PW_LOG(level,
+         PW_LOG_DEFAULT_FLAGS,
          "        Actual: %s",
          expectation.evaluated_expression);
 }