title: Boost logging, filter by named scope
body: <p>I am using boost log in my application, and while it has been tricky to configure it is generally working well. </p>
  
  <p>Now however, I would like to add some more advanced filtering logic to my application, and I can't figure it out. </p>
  
  <p>What I would like is to have two "levels" of filtering: </p>
  
  <ol>
  <li>I am already using a "severity logger" with different levels like <code>debug</code>, <code>warn</code>, <code>note</code> etc. This is setup and working.</li>
  <li>I would like to add an additional way to filter records by looking at the "named scope" that the record emanates from.</li>
  </ol>
  
  <p>So I would like for instance to be able to see only the records with severity >= <code>note</code>, AND within a <code>NAMED_SCOPE</code> of <code>monthly</code>.</p>
  
  <p>I have successfully been able to use the <code>BOOST_LOG_NAMED_SCOPE()</code> macro and can see the scope stack in the log messages.</p>
  
  <p>I have tried implementing a custom filter with <code>boost::phoenix</code>, but I can't get it to work. </p>
  
  <p>The code I have pasted here compiles in my application (I am working on stripping this down so I can include a complete working minimal example here), but nothing appears to happen in the <code>my_filter(..)</code> function. I think that I am not correctly passing the scope stack into the bound function, because if I put a <code>std::cout</code> statement within the loop over the scope stack, I do not see anything printed.</p>
  
  <p>Minimal example here:</p>
  
  <pre class="lang-c++ prettyprint-override"><code>// Excerpt from MyLogger.cpp class
  
  bool my_filter(attrs::named_scope_list const&amp; scopeList) {
    for (attrs::named_scope_list::const_iterator iter = scopeList.begin(); iter != scopeList.end(); ++iter) {
      if ( (*iter).scope_name == "monthly") {
        return true;
      }
    }
    return false;
  }
  
  void setup_logging(std::string lvl) {
  
    logging::core::get()-&gt;add_global_attribute(
      "Scope", attrs::named_scope()
    );
  
    logging::add_console_log(
      std::clog,
      keywords::format = (
        expr::stream
          &lt;&lt; "[" &lt;&lt; severity &lt;&lt; "] "
          &lt;&lt; "[" &lt;&lt; named_scope &lt;&lt; "] "
          &lt;&lt; expr::smessage
      )
    );
  
    try {
      // set the severity level...
      EnumParser&lt;severity_level&gt; parser;
      logging::core::get()-&gt;set_filter(
        (
          severity &gt;= parser.parseEnum(lvl) &amp;&amp;
          ( expr::has_attr("Scope") &amp;&amp; ( boost::phoenix::bind(&amp;my_filter, attrs::named_scope::get_scopes() ) ) )
        )
      );
    } catch (std::runtime_error&amp; e) {
      std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
      std::cout &lt;&lt; "'" &lt;&lt; lvl &lt;&lt; "' is an invalid --log-level! Must be one of "
                &lt;&lt; "[debug, info, note, warn, err, fatal]\n";
      exit(-1);
    }
  
  }
  </code></pre>
  
  <p><strong>EDIT</strong> Updated with minimal working example:</p>
  
  <h3>TEMLogger.h</h3>
  
  <pre><code>#ifndef _TEMLOGGER_H_
  #define _TEMLOGGER_H_
  
  #include &lt;string&gt;
  #include &lt;iostream&gt;
  #include &lt;sstream&gt;
  #include &lt;cstdlib&gt;
  #include &lt;exception&gt;
  #include &lt;map&gt;
  #include &lt;iomanip&gt;
  
  #include &lt;boost/log/core.hpp&gt;
  #include &lt;boost/log/trivial.hpp&gt;
  #include &lt;boost/log/sources/global_logger_storage.hpp&gt;
  #include &lt;boost/log/sources/severity_feature.hpp&gt;
  #include &lt;boost/log/sources/severity_logger.hpp&gt;
  #include &lt;boost/log/expressions.hpp&gt;
  #include &lt;boost/log/utility/setup/console.hpp&gt;
  
  #include &lt;boost/log/attributes/current_process_id.hpp&gt;
  #include &lt;boost/log/attributes/scoped_attribute.hpp&gt;
  
  
  namespace logging = boost::log;
  namespace src = boost::log::sources;
  namespace attrs = boost::log::attributes;
  namespace keywords = boost::log::keywords;
  namespace expr = boost::log::expressions;
  namespace sinks = boost::log::sinks;
  
  /** Define the "severity levels" for Boost::Log's severity logger. */
  enum severity_level {
    debug, info, note, warn, err, fatal
  };
  
  /** Convert from string to enum integer value.
   *
   * Inspired by: http://stackoverflow.com/questions/726664/string-to-enum-in-c
   */
  template &lt;typename T&gt;
  class EnumParser {
      std::map &lt;std::string, T&gt; enumMap;
  public:
      EnumParser(){};
  
      T parseEnum(const std::string &amp;value) { 
          typename std::map&lt;std::string, T&gt;::const_iterator iValue = enumMap.find(value);
          if (iValue == enumMap.end())
              throw std::runtime_error("Value not found in enum!");
          return iValue-&gt;second;
      }
  };
  
  
  BOOST_LOG_GLOBAL_LOGGER(my_logger, src::severity_logger&lt; severity_level &gt;);
  
  /** Send string representing an enum value to stream 
   */
  std::ostream&amp; operator&lt;&lt; (std::ostream&amp; strm, severity_level lvl);
  
  void setup_logging(std::string lvl);
  
  #endif /* _TEMLOGGER_H_ */
  </code></pre>
  
  <h3>TEMLogger.cpp</h3>
  
  <pre><code>#include &lt;boost/log/expressions/formatters/named_scope.hpp&gt;
  #include &lt;boost/log/expressions.hpp&gt;
  #include &lt;boost/phoenix.hpp&gt;
  
  #include "TEMLogger.h"
  
  // Create the global logger object
  src::severity_logger&lt; severity_level &gt; glg;
  
  // Add a bunch of attributes to it
  BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
  BOOST_LOG_ATTRIBUTE_KEYWORD(named_scope, "Scope", attrs::named_scope::value_type)
  
  /** Initialize the enum parser map from strings to the enum levels.*/
  template&lt;&gt;
  EnumParser&lt; severity_level &gt;::EnumParser() {
      enumMap["debug"] = debug;
      enumMap["info"] = info;
      enumMap["note"] = note;
      enumMap["warn"] = warn;
      enumMap["err"] = err;
      enumMap["fatal"] = fatal;
  }
  
  std::ostream&amp; operator&lt;&lt; (std::ostream&amp; strm, severity_level level) {
      static const char* strings[] = { 
        "debug", "info", "note", "warn", "err", "fatal"
      };
  
      if (static_cast&lt; std::size_t &gt;(level) &lt; sizeof(strings) / sizeof(*strings))
          strm &lt;&lt; strings[level];
      else
          strm &lt;&lt; static_cast&lt; int &gt;(level);
  
      return strm;
  }
  
  bool my_filter(boost::log::value_ref&lt; attrs::named_scope &gt; const&amp; theNamedScope) {
  
    // I think I want something like this:
    // for (attrs::named_scope_list::const_iterator iter = scopeList.begin(); iter != scopeList.end(); ++iter) {
    //   if ( (*iter).scope_name == "monthly"){
    //     return true;
    //   }
    // }
    return true;
  }
  
  void setup_logging(std::string lvl) {
  
    logging::core::get()-&gt;add_global_attribute(
      "Scope", attrs::named_scope()
    );
  
    logging::add_console_log(
      std::clog,
      keywords::format = (
        expr::stream
          &lt;&lt; "[" &lt;&lt; severity &lt;&lt; "] "
          &lt;&lt; "[" &lt;&lt; named_scope &lt;&lt; "] "
          &lt;&lt; expr::smessage
      )
    );
  
    try {
      // set the severity level...
      EnumParser&lt;severity_level&gt; parser;
      logging::core::get()-&gt;set_filter(
        (
          severity &gt;= parser.parseEnum(lvl) &amp;&amp;
          ( expr::has_attr("Scope") &amp;&amp; ( boost::phoenix::bind(&amp;my_filter, expr::attr&lt; attrs::named_scope &gt;("Scope").or_none()) ) )
        )
      );
    } catch (std::runtime_error&amp; e) {
      std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
      std::cout &lt;&lt; "'" &lt;&lt; lvl &lt;&lt; "' is an invalid --log-level! Must be one of "
                &lt;&lt; "[debug, info, note, warn, err, fatal]\n";
      exit(-1);
    }
  
  }
  </code></pre>
  
  <h3>Main Program</h3>
  
  <pre><code>#include "TEMLogger.h"
  
  extern src::severity_logger&lt; severity_level &gt; glg;
  
  void func1() {
    BOOST_LOG_NAMED_SCOPE("monthly");
    for (int i=0; i&lt;5; ++i) {
      BOOST_LOG_SEV(glg, note) &lt;&lt; "doing iteration " &lt;&lt; i &lt;&lt; "within monthly scope!";
    }
  
  }
  
  int main(int argc, char* argv[]) {
  
    std::cout &lt;&lt; "Setting up logging...\n";
  
    setup_logging("debug");
  
    BOOST_LOG_SEV(glg, note) &lt;&lt; "Some message in the main scope";
  
    func1();
  
    BOOST_LOG_SEV(glg, note) &lt;&lt; "Another message in the main scope";
  
    return 0;
  }
  </code></pre>
  
  <h3>Compile</h3>
  
  <p>(I am on a Mac, and due to the way I installed Boost I have to specify the compiler, and the method of linking the Boost libs. YMMV)</p>
  
  <pre><code>g++-4.8 -o TEMLogger.o -c -g -DBOOST_ALL_DYN_LINK TEMLogger.cpp
  g++-4.8 -o log-filter-example.o -c -g -DBOOST_ALL_DYN_LINK log-filter-example.cpp
  g++-4.8 -o a.out log-filter-example.o TEMLogger.o -L/usr/local/lib -lboost_system-mt -lboost_filesystem-mt -lboost_thread-mt -lboost_log-mt
  </code></pre>
  
  <h3>Run</h3>
  
  <pre><code>$ ./a.out 
  Setting up logging...
  [note] [] Some message in the main scope
  [note] [monthly] doing iteration 0within monthly scope!
  [note] [monthly] doing iteration 1within monthly scope!
  [note] [monthly] doing iteration 2within monthly scope!
  [note] [monthly] doing iteration 3within monthly scope!
  [note] [monthly] doing iteration 4within monthly scope!
  [note] [] Another message in the main scope
  </code></pre>