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

RoutingAppender can be configured with scripts

Attach filesAttach ScreenshotAdd voteVotersWatch issueWatchersCreate sub-taskLinkCloneUpdate Comment AuthorReplace String in CommentUpdate Comment VisibilityDelete Comments
    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Open
    • Major
    • Resolution: Unresolved
    • None
    • None
    • None
    • 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

          This comment will be Viewable by All Users Viewable by All Users
          Cancel

          People

            ggregory Gary D. Gregory
            ggregory Gary D. Gregory

            Dates

              Created:
              Updated:

              Slack

                Issue deployment