You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by "Gary Gregory (JIRA)" <ji...@apache.org> on 2016/09/14 03:56:20 UTC

[jira] [Updated] (LOG4J2-1578) RoutingAppender can be configured with scripts

     [ https://issues.apache.org/jira/browse/LOG4J2-1578?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Gary Gregory updated LOG4J2-1578:
---------------------------------
    Description: 
RoutingAppender can be configured with scripts. 

h2. Use Case
What I need to do is only add a specific appender when running on a specific OS (USS on OS/390 if you must know). Then only add a different appender when not running on that OS.

h2. Final Solution
Example configuration:
{code:xml}
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
    <Flume name="AuditLogger" compress="true">
      <Agent host="192.168.10.101" port="8800"/>
      <Agent host="192.168.10.102" port="8800"/>
      <RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
    </Flume>
    <Routing name="Routing">
      <Script name="RoutingInit" language="groovy"><![CDATA[
         if (System.getProperty(?os.name?).contains(?OS/390") {
            return "OS390";
         }
         return null;]]>
      </Script>
      <Routes>     
        <Script name="Router" language="groovy"><![CDATA[
            if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("AUDIT")) {
                return "AUDIT";
            } else if (logEvent.getContextMap().containsKey("UserId")) { 
                return logEvent.getContextMap().get("UserId");
            }
            return "STDOUT";
            ]]>
          </Script> 
        <Route>
          <OS390Appender name="OS390-${mdc:UserId"/> 
          <RollingFile name="Rolling-${mdc:UserId}" fileName="${mdc:UserId}.log"
                       filePattern="${mdc:UserId}.%i.log.gz">
            <PatternLayout>
              <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
            <SizeBasedTriggeringPolicy size="500" />
          </RollingFile>
        </Route>
        <Route ref="AuditLogger" key="Audit"/>
        <Route ref="STDOUT" key="STDOUT"/>
      </Routes>
      <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
    </Routing>
  </Appenders>
{code}

As part of this the {{Routes}} class' factory method is deprecated in favor of a builder.

h2. Initial Solution
Posted by [~ralphgoers] on the dev ML:

After reviewing what I wrote below and looking at the Routing Appender I think the best thing to do is just to add script support to it.  It already has support for a default Route. The init script, if present, could override which Route to use as I described below. Then we could add a script attribute to the Routes plugin which could be used to select the Route instead of only matching on the ThreadContext key.

With that I think you would have everything you want, plus it could be used as a more intelligent way to route to existing appenders.

The configuration would then look like:
{code:xml}
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
    <Flume name="AuditLogger" compress="true">
      <Agent host="192.168.10.101" port="8800"/>
      <Agent host="192.168.10.102" port="8800"/>
      <RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
    </Flume>
    <Routing name="Routing">
      <InitScript name="RoutingInit" language="groovy"><![CDATA[
         if (System.getProperty(?os.name?).contains(?OS/390") {
            return "OS390";
         }
         return null;]]>
      </InitScript>
      <Routes>     
        <Script name="Router" language="groovy"><![CDATA[
            if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("AUDIT")) {
                return "AUDIT";
            } else if (logEvent.getContextMap().containsKey("UserId")) { 
                return logEvent.getContextMap().get("UserId");
            }
            return "STDOUT";
            ]]>
          </Script> 
        <Route>
          <OS390Appender name="OS390-${mdc:UserId"/> 
          <RollingFile name="Rolling-${mdc:UserId}" fileName="${mdc:UserId}.log"
                       filePattern="${mdc:UserId}.%i.log.gz">
            <PatternLayout>
              <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
            <SizeBasedTriggeringPolicy size="500" />
          </RollingFile>
        </Route>
        <Route ref="AuditLogger" key="Audit"/>
        <Route ref="STDOUT" key="STDOUT"/>
      </Routes>
      <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
    </Routing>
  </Appenders>
{code}
First, the init script changes the default route based on the OS.
Second, notice that "Routes" has a new Script element and does not have a pattern specified, so the script is determining the key instead of the pattern. 
Third, the real default route is now "STDOUT" since the actual default Route is only referenced when a UserId is present in the thread context map.

What would also be nice is if there was a way to have the returned value be usable as a Lookup value in the default Appender definition, instead of relying on the MDC as the code above does. I should be able to pick something out of the message itself and use that as the key. That should be doable but I am still pondering how I would implement that.



  was:
RoutingAppender can be configured with scripts. 

h2. Use Case
What I need to do is only add a specific appender when running on a specific OS (USS on OS/390 if you must know). Then only add a different appender when not running on that OS.

h2. Solution
Posted by [~ralphgoers] on the dev ML:

After reviewing what I wrote below and looking at the Routing Appender I think the best thing to do is just to add script support to it.  It already has support for a default Route. The init script, if present, could override which Route to use as I described below. Then we could add a script attribute to the Routes plugin which could be used to select the Route instead of only matching on the ThreadContext key.

With that I think you would have everything you want, plus it could be used as a more intelligent way to route to existing appenders.

The configuration would then look like:
{code:xml}
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
    <Flume name="AuditLogger" compress="true">
      <Agent host="192.168.10.101" port="8800"/>
      <Agent host="192.168.10.102" port="8800"/>
      <RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
    </Flume>
    <Routing name="Routing">
      <InitScript name="RoutingInit" language="groovy"><![CDATA[
         if (System.getProperty(?os.name?).contains(?OS/390") {
            return "OS390";
         }
         return null;]]>
      </InitScript>
      <Routes>     
        <Script name="Router" language="groovy"><![CDATA[
            if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("AUDIT")) {
                return "AUDIT";
            } else if (logEvent.getContextMap().containsKey("UserId")) { 
                return logEvent.getContextMap().get("UserId");
            }
            return "STDOUT";
            ]]>
          </Script> 
        <Route>
          <OS390Appender name="OS390-${mdc:UserId"/> 
          <RollingFile name="Rolling-${mdc:UserId}" fileName="${mdc:UserId}.log"
                       filePattern="${mdc:UserId}.%i.log.gz">
            <PatternLayout>
              <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
            <SizeBasedTriggeringPolicy size="500" />
          </RollingFile>
        </Route>
        <Route ref="AuditLogger" key="Audit"/>
        <Route ref="STDOUT" key="STDOUT"/>
      </Routes>
      <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
    </Routing>
  </Appenders>
{code}
First, the init script changes the default route based on the OS.
Second, notice that "Routes" has a new Script element and does not have a pattern specified, so the script is determining the key instead of the pattern. 
Third, the real default route is now "STDOUT" since the actual default Route is only referenced when a UserId is present in the thread context map.

What would also be nice is if there was a way to have the returned value be usable as a Lookup value in the default Appender definition, instead of relying on the MDC as the code above does. I should be able to pick something out of the message itself and use that as the key. That should be doable but I am still pondering how I would implement that.




> RoutingAppender can be configured with scripts
> ----------------------------------------------
>
>                 Key: LOG4J2-1578
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-1578
>             Project: Log4j 2
>          Issue Type: New Feature
>            Reporter: Gary Gregory
>            Assignee: Gary Gregory
>
> RoutingAppender can be configured with scripts. 
> h2. Use Case
> What I need to do is only add a specific appender when running on a specific OS (USS on OS/390 if you must know). Then only add a different appender when not running on that OS.
> h2. Final Solution
> Example configuration:
> {code:xml}
>   <Appenders>
>     <Console name="STDOUT" target="SYSTEM_OUT">
>       <PatternLayout pattern="%m%n"/>
>     </Console>
>     <Flume name="AuditLogger" compress="true">
>       <Agent host="192.168.10.101" port="8800"/>
>       <Agent host="192.168.10.102" port="8800"/>
>       <RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
>     </Flume>
>     <Routing name="Routing">
>       <Script name="RoutingInit" language="groovy"><![CDATA[
>          if (System.getProperty(?os.name?).contains(?OS/390") {
>             return "OS390";
>          }
>          return null;]]>
>       </Script>
>       <Routes>     
>         <Script name="Router" language="groovy"><![CDATA[
>             if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("AUDIT")) {
>                 return "AUDIT";
>             } else if (logEvent.getContextMap().containsKey("UserId")) { 
>                 return logEvent.getContextMap().get("UserId");
>             }
>             return "STDOUT";
>             ]]>
>           </Script> 
>         <Route>
>           <OS390Appender name="OS390-${mdc:UserId"/> 
>           <RollingFile name="Rolling-${mdc:UserId}" fileName="${mdc:UserId}.log"
>                        filePattern="${mdc:UserId}.%i.log.gz">
>             <PatternLayout>
>               <pattern>%d %p %c{1.} [%t] %m%n</pattern>
>             </PatternLayout>
>             <SizeBasedTriggeringPolicy size="500" />
>           </RollingFile>
>         </Route>
>         <Route ref="AuditLogger" key="Audit"/>
>         <Route ref="STDOUT" key="STDOUT"/>
>       </Routes>
>       <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
>     </Routing>
>   </Appenders>
> {code}
> As part of this the {{Routes}} class' factory method is deprecated in favor of a builder.
> h2. Initial Solution
> Posted by [~ralphgoers] on the dev ML:
> After reviewing what I wrote below and looking at the Routing Appender I think the best thing to do is just to add script support to it.  It already has support for a default Route. The init script, if present, could override which Route to use as I described below. Then we could add a script attribute to the Routes plugin which could be used to select the Route instead of only matching on the ThreadContext key.
> With that I think you would have everything you want, plus it could be used as a more intelligent way to route to existing appenders.
> The configuration would then look like:
> {code:xml}
>   <Appenders>
>     <Console name="STDOUT" target="SYSTEM_OUT">
>       <PatternLayout pattern="%m%n"/>
>     </Console>
>     <Flume name="AuditLogger" compress="true">
>       <Agent host="192.168.10.101" port="8800"/>
>       <Agent host="192.168.10.102" port="8800"/>
>       <RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
>     </Flume>
>     <Routing name="Routing">
>       <InitScript name="RoutingInit" language="groovy"><![CDATA[
>          if (System.getProperty(?os.name?).contains(?OS/390") {
>             return "OS390";
>          }
>          return null;]]>
>       </InitScript>
>       <Routes>     
>         <Script name="Router" language="groovy"><![CDATA[
>             if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("AUDIT")) {
>                 return "AUDIT";
>             } else if (logEvent.getContextMap().containsKey("UserId")) { 
>                 return logEvent.getContextMap().get("UserId");
>             }
>             return "STDOUT";
>             ]]>
>           </Script> 
>         <Route>
>           <OS390Appender name="OS390-${mdc:UserId"/> 
>           <RollingFile name="Rolling-${mdc:UserId}" fileName="${mdc:UserId}.log"
>                        filePattern="${mdc:UserId}.%i.log.gz">
>             <PatternLayout>
>               <pattern>%d %p %c{1.} [%t] %m%n</pattern>
>             </PatternLayout>
>             <SizeBasedTriggeringPolicy size="500" />
>           </RollingFile>
>         </Route>
>         <Route ref="AuditLogger" key="Audit"/>
>         <Route ref="STDOUT" key="STDOUT"/>
>       </Routes>
>       <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
>     </Routing>
>   </Appenders>
> {code}
> First, the init script changes the default route based on the OS.
> Second, notice that "Routes" has a new Script element and does not have a pattern specified, so the script is determining the key instead of the pattern. 
> Third, the real default route is now "STDOUT" since the actual default Route is only referenced when a UserId is present in the thread context map.
> What would also be nice is if there was a way to have the returned value be usable as a Lookup value in the default Appender definition, instead of relying on the MDC as the code above does. I should be able to pick something out of the message itself and use that as the key. That should be doable but I am still pondering how I would implement that.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org