Logging & Error Handling
Integrated logging with centralized exception management and customizable error pages.
Structured Logging
OorianLog provides a lightweight logging facade built on the JDK's System.Logger SPI. Consistent, structured output with pluggable backends.
Centralized Errors
Unhandled exceptions are caught at the framework level, logged with full context, and routed to your custom error page.
Custom Error Pages
Define your own error pages with full access to the exception and HTTP status code for branded, user-friendly error displays.
Log Levels
Five levels—TRACE, DEBUG, INFO, WARNING, ERROR—give you precise control over verbosity in development and production.
Request Context
Exceptions are logged with the originating page, session ID, and request details for fast debugging in production.
Structured Logging
Oorian provides the OorianLog facade built on the JDK's System.Logger SPI. Out of the box it delegates to java.util.logging, but you can plug in SLF4J, Log4j2, or any other backend by adding a single bridge dependency—no Oorian configuration required.
public class OrderPage extends HtmlPage
{
private static final OorianLog LOG =
OorianLog.getLogger(OrderPage.class);
@Override
public void onEvent(FormEvent event)
{
Parameters params = event.getParameters();
String orderId = params.getParameterValue("orderId");
String userId = getSession().getAttribute("userId");
LOG.info("Processing order {0} for user {1}", orderId, userId);
try
{
processOrder(orderId);
LOG.debug("Order {0} completed successfully", orderId);
}
catch (Exception ex)
{
LOG.error("Failed to process order", ex);
}
}
}Log Levels
OorianLog provides five levels—TRACE, DEBUG, INFO, WARNING, and ERROR—each mapped to a System.Logger.Level. Use verbose levels during development to trace execution, then dial back to operational levels in production.
Development
// Fine-grained diagnostic output
LOG.debug("Processing page {0} for session {1}",
pageId, sessionId);
LOG.trace("WebSocket message received: {0} bytes", length);
// Guard expensive construction
if (LOG.isLoggable(System.Logger.Level.DEBUG))
{
String details = buildExpensiveDebugInfo(request);
LOG.debug("Request details: {0}", details);
}Production
// Lifecycle events
LOG.info("Application started on port 8443");
LOG.info("Registered {0} pages", pageCount);
// Recoverable issues
LOG.warning("CSRF validation failed for session {0}",
sessionId);
// Failures
LOG.error("Payment gateway unreachable", ex);Centralized Exception Handler
Implement the ExceptionHandler interface to create a single point of control for all unhandled exceptions. The handler receives the exception and the page context (if available), giving you a clean place to add logging, alerting, and monitoring without modifying individual pages.
public class AppExceptionHandler implements ExceptionHandler
{
private static final OorianLog LOG =
OorianLog.getLogger(AppExceptionHandler.class);
@Override
public void handle(Exception exception, HtmlPage page)
{
LOG.error("Unhandled exception", exception);
// Include page context when available
if (page != null)
{
LOG.error("Exception on page: {0}",
page.getClass().getName());
}
// Send an alert (e.g., email, Slack, PagerDuty)
alertService.notify(exception);
}
}Registering the Handler
Register your handler in Application.initialize(). The framework invokes it during page creation, event processing, and worker thread execution.
@Override
protected void initialize(ServletContext context)
{
registerPackage("com.myapp");
setExceptionHandler(new AppExceptionHandler());
}The handler integrates at three points: page creation (createHead, createBody), WebSocket/AJAX/SSE message handling, and OorianWorkerThread execution. It is invoked before the page-level onException() hook, so you can centralize monitoring without touching individual pages.
Custom Error Pages
Build branded error pages using the same Oorian elements you use everywhere else. Extend ErrorPage and implement createBody(Body) to build your content. The base class handles the HTML skeleton, charset, viewport, and a minimal CSS reset.
public class NotFoundPage extends ErrorPage
{
@Override
protected void createBody(Body body)
{
H1 title = new H1("Error " + getStatusCode());
title.setColor("#ea4335");
title.setTextAlign("center");
body.addElement(title);
Paragraph message = new Paragraph(getMessage());
message.setTextAlign("center");
body.addElement(message);
Paragraph path = new Paragraph("Path: " + getRequestPath());
path.setColor("#6b7280");
path.setTextAlign("center");
body.addElement(path);
}
}Registering Error Pages
Register your error pages in Application.initialize(). You can assign pages to specific HTTP status codes, or set a default that handles any unregistered code. The lookup order is: status-specific page, then default error page, then the built-in DefaultErrorPage.
@Override
protected void initialize(ServletContext context)
{
registerPackage("com.myapp");
// Register error pages for specific status codes
setErrorPage(404, NotFoundPage.class);
setErrorPage(403, ForbiddenPage.class);
setErrorPage(500, ServerErrorPage.class);
// Catch-all for any status code without a specific page
setDefaultErrorPage(GenericErrorPage.class);
}Available Methods
The ErrorPage base class provides convenience getters so you can tailor the display based on what went wrong:
getStatusCode()
The HTTP status code (e.g., 404, 500). Use this to vary the display by error type.
getRequestPath()
The original request path that triggered the error. May be null.
getException()
The exception that caused the error, if any. Null for status-only errors like 404.
getTitle() / getMessage()
Human-readable title and description for the status code (e.g., "Page Not Found").
To add custom CSS, override createHead(Head head) and add a Style element. Error pages are lightweight and standalone—they avoid WebSocket/event overhead and render reliably even when the normal page infrastructure has failed.
Benefits
Built on System.Logger
OorianLog delegates to the JDK's System.Logger SPI—plug in SLF4J, Log4j2, or use the default java.util.logging backend with zero configuration.
Centralized Error Handling
All unhandled exceptions flow through a single error handler. No scattered try-catch blocks or missed exceptions—every error is captured and logged.
Custom Error Pages
Build branded error pages as regular Oorian pages with full access to the exception, status code, and request details. Users see helpful messages, not stack traces.
Request Context
Every logged exception includes the page class, session ID, and request details. Correlate errors to specific user actions without manual instrumentation.
No External Dependencies
Zero additional libraries required for logging or error handling. Everything is built into the framework using standard Java APIs.