Uploaded image for project: 'Log4j 2'
  1. Log4j 2
  2. LOG4J2-1578

RoutingAppender can be configured with scripts

    Details

    • Type: New Feature
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      RoutingAppender can be configured with scripts.

      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.

      Final Solution

      Example configuration:

        <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>
      

      As part of this the Routes class' factory method org.apache.logging.log4j.core.appender.routing.Routes.createRoutes(String, Route...) is deprecated in favor of a builder.

      Initial Solution

      Posted by Ralph Goers 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:

        <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>
      

      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.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                garydgregory Gary Gregory
                Reporter:
                garydgregory Gary Gregory
              • Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                • Created:
                  Updated: