Skip to content

Commit

Permalink
Issue #9980 - add additional codes for CustomRequestLog
Browse files Browse the repository at this point in the history
Signed-off-by: Lachlan Roberts <[email protected]>
  • Loading branch information
lachlan-roberts committed Sep 27, 2024
1 parent 8aa9c8b commit c8c891b
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.pathmap.PathMappings;
import org.eclipse.jetty.util.DateCache;
Expand Down Expand Up @@ -201,7 +202,6 @@
* <p>The query string, prepended with a ? if a query string exists, otherwise an empty string.</p>
* </td>
* </tr>
* <!-- TODO ATTRIBUTE LOGGING -->
* <tr>
* <td>%r</td>
* <td>
Expand All @@ -214,13 +214,13 @@
* <p>The name of the Handler or Servlet generating the response (if any).</p>
* </td>
* </tr>
* <tr>
* <tr>
* <td>%s</td>
* <td>
* <p>The HTTP response status code.</p>
* </td>
* </tr>
* <tr>
* <tr>
* <td>%{format|timeZone|locale}t</td>
* <td>
* <p>The time at which the request was received.</p>
Expand Down Expand Up @@ -262,7 +262,7 @@
* <p>The URL path requested, not including any query string.</p>
* </td>
* </tr>
* <tr>
* <tr>
* <td>%X</td>
* <td>
* <p>The connection status when response is completed:</p>
Expand All @@ -288,6 +288,36 @@
* <p>The value of the VARNAME response trailer.</p>
* </td>
* </tr>
* <tr>
* <td>%A</td>
* <td>
* <p>The Request authority (which might differ from the Host header).</p>
* </td>
* </tr>
* <tr>
* <td>%h</td>
* <td>
* <p>The Request scheme.</p>
* </td>
* </tr>
* <tr>
* <td>%uri</td>
* <td>
* <p>The entire Request HttpURI (excluding query).</p>
* </td>
* </tr>
* <tr>
* <td>%{q}uri</td>
* <td>
* <p>The entire Request HttpURI (including query).</p>
* </td>
* </tr>
* <tr>
* <td>%{attributeName}attr</td>
* <td>
* <p>A request attribute.</p>
* </td>
* </tr>
* </table>
* <!-- end::documentation[] -->
*/
Expand All @@ -309,6 +339,7 @@ public record LogDetail(String handlerName, String realPath)
public static final String LOG_DETAIL = CustomRequestLog.class.getName() + ".logDetail";
private static final Logger LOG = LoggerFactory.getLogger(CustomRequestLog.class);
private static final ThreadLocal<StringBuilder> _buffers = ThreadLocal.withInitial(() -> new StringBuilder(256));
private static final Pattern PATTERN = Pattern.compile("^(?:%(?<MOD>!?[0-9,]+)?(?:\\{(?<ARG>[^}]+)})?(?<CODE>(?:(?:ti)|(?:to)|(?:uri)|(?:attr)|[a-zA-Z%]))|(?<LITERAL>[^%]+))(?<REMAINING>.*)", Pattern.DOTALL | Pattern.MULTILINE);

private final RequestLog.Writer _requestLogWriter;
private final MethodHandle _logHandle;
Expand Down Expand Up @@ -444,7 +475,7 @@ protected void doStart() throws Exception

private static void append(StringBuilder buf, String s)
{
if (s == null || s.length() == 0)
if (s == null || s.isEmpty())
buf.append('-');
else
buf.append(s);
Expand Down Expand Up @@ -489,8 +520,6 @@ private static List<Token> getTokens(String formatString)
{PARAM} is an optional string parameter to the percent code.
CODE is a 1 to 2 character string corresponding to a format code.
*/
final Pattern PATTERN = Pattern.compile("^(?:%(?<MOD>!?[0-9,]+)?(?:\\{(?<ARG>[^}]+)})?(?<CODE>(?:(?:ti)|(?:to)|[a-zA-Z%]))|(?<LITERAL>[^%]+))(?<REMAINING>.*)", Pattern.DOTALL | Pattern.MULTILINE);

List<Token> tokens = new ArrayList<>();
String remaining = formatString;
while (remaining.length() > 0)
Expand Down Expand Up @@ -788,6 +817,27 @@ else if ("d".equals(arg))

yield lookup.findStatic(CustomRequestLog.class, "logResponseTrailer", logTypeArg).bindTo(arg);
}
case "A" -> lookup.findStatic(CustomRequestLog.class, "logRequestAuthority", logType);
case "h" -> lookup.findStatic(CustomRequestLog.class, "logRequestScheme", logType);
case "uri" ->
{
if (arg == null)
arg = "";
String method = switch (arg)
{
case "" -> "logRequestHttpUri";
case "q" -> "logRequestHttpUriQuery";
default -> throw new IllegalArgumentException("Invalid arg for %uri");
};

yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "attr" ->
{
MethodType logRequestAttribute = methodType(void.class, String.class, StringBuilder.class, Request.class, Response.class);
yield lookup.findStatic(CustomRequestLog.class, "logRequestAttribute", logRequestAttribute).bindTo(arg);
}

default -> throw new IllegalArgumentException("Unsupported code %" + code);
};

Expand Down Expand Up @@ -1139,4 +1189,43 @@ private static void logResponseTrailer(String arg, StringBuilder b, Request requ
else
b.append('-');
}

@SuppressWarnings("unused")
private static void logRequestAuthority(StringBuilder b, Request request, Response response)
{
HttpURI httpURI = request.getHttpURI();
if (httpURI.hasAuthority())
append(b, httpURI.getAuthority());
else
b.append('-');
}

@SuppressWarnings("unused")
private static void logRequestScheme(StringBuilder b, Request request, Response response)
{
append(b, request.getHttpURI().getScheme());
}

@SuppressWarnings("unused")
private static void logRequestHttpUri(StringBuilder b, Request request, Response response)
{
HttpURI.Mutable uri = HttpURI.build(request.getHttpURI()).query(null);
append(b, uri.toString());
}

@SuppressWarnings("unused")
private static void logRequestHttpUriQuery(StringBuilder b, Request request, Response response)
{
append(b, request.getHttpURI().toString());
}

@SuppressWarnings("unused")
private static void logRequestAttribute(String arg, StringBuilder b, Request request, Response response)
{
Object attribute = request.getAttribute(arg);
if (attribute != null)
append(b, attribute.toString());
else
b.append('-');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,107 @@ public boolean handle(Request request, Response response, Callback callback)
assertThat(log, is("42"));
}

@Test
public void testLogRequestAuthority() throws Exception
{
start("%A", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
Content.Sink.write(response, false, "hello", Callback.NOOP);
callback.succeeded();
return true;
}
});

HttpTester.Response response = getResponse("GET / HTTP/1.0\n\n");
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertThat(log, is("127.0.0.1:" + _serverConnector.getLocalPort()));
}

@Test
public void testLogRequestScheme() throws Exception
{
start("%h", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
Content.Sink.write(response, false, "hello", Callback.NOOP);
callback.succeeded();
return true;
}
});

HttpTester.Response response = getResponse("GET / HTTP/1.0\n\n");
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertThat(log, is("http"));
}

@Test
public void testLogRequestHttpUri() throws Exception
{
start("%uri", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
Content.Sink.write(response, false, "hello", Callback.NOOP);
callback.succeeded();
return true;
}
});

HttpTester.Response response = getResponse("GET /?hello=world HTTP/1.0\n\n");
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertThat(log, is("http://127.0.0.1:" + _serverConnector.getLocalPort() + "/"));
}

@Test
public void testLogRequestHttpUriWithQuery() throws Exception
{
start("%{q}uri", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
Content.Sink.write(response, false, "hello", Callback.NOOP);
callback.succeeded();
return true;
}
});

HttpTester.Response response = getResponse("GET /?hello=world HTTP/1.0\n\n");
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertThat(log, is("http://127.0.0.1:" + _serverConnector.getLocalPort() + "/?hello=world"));
}

@Test
public void testLogRequestAttribute() throws Exception
{
start("%{myAttribute}attr", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
request.setAttribute("myAttribute", "value1234");
Content.Sink.write(response, false, "hello", Callback.NOOP);
callback.succeeded();
return true;
}
});

HttpTester.Response response = getResponse("GET /?hello=world HTTP/1.0\n\n");
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertThat(log, is("value1234"));
}

class TestRequestLogWriter implements RequestLog.Writer
{
@Override
Expand Down

0 comments on commit c8c891b

Please sign in to comment.