Event Logging
Event Logging is a Java API for logging audit events conforming to the Event Logging XML Schema. The API uses a generated Java JAXB model of the Event Logging XML Schema. Event Logging can be incorporated into your Java application to provide a means of recording and outputting audit events or user actions for compliance, security or monitoring.
As this library is essentially a Java representation of the Event Logging XML Schema it helps to refer to the schema to better understand the Java model. The schema documentation provides a lot of detail about how to model events using the schema which will be helpful when using this library.
The Javadoc for the latest release of the library is available here.
This library requires Java 8 as a minimum.
The only dependencies it brings with it are javax.xml.bind:jaxb-api
and org.slf4j:slf4j-api
.
By default the created events are serialised to XML and passed to a SLF4J logger which would typically be linked to a rolling file appender.
What to Log
The aim of this API is to capture the following information about an action/event.
- Who - Who performed the action, i.e the user ID or some other identifer to link the event to a user and details of the user's device.
- Where - Where did the event happen, i.e on what system, device, network address.
- What - The detail of what they did, e.g. copy a file named X from A to B.
- When - The time the event happend.
- Why - The purpose and/or justification for their action, e.g. where compliance rules dictate that certain actions have prior approval. The requirement to capture the why is dependant on the rules in place for the system using this library.
Using the Event Logging API
Gradle/Maven dependencies
The Event Logging API is available as a Maven/Gradle dependency on Bintray. You will need to add our Bintray repository to your build tool. To include it in your Gradle build add the following:
repositories {
//...
maven { url "https://dl.bintray.com/stroom/event-logging" }
}
dependencies {
compile 'event-logging:event-logging:v5.0-beta.9_schema-v4.0-beta.1'
}
NOTE:
Version 3.x.x+ of event-logging is compatible with Java 8+
The second version number in the version string is the version of the event-logging-schema XML Schema that the library uses.
Example Application
A standalone example application can be found in ./example-logged-application
that shows how you can include logging in your application.
See here for details.
Calling the API
The API has two main parts, the class model that represents the schema and the classes involved with serialising and logging the events.
Event Class Model
The class model is autogenerated from the XML schema and includes fluent builder classes and methods to make constructing an event easier.
The top level class for constructing an event is event.logging.Event
.
final Event event = Event.builder()
//...
.build();
Logging Service
The interface for logging audit events is EventLoggingService.java
.
A default implementation of this interface is included in the form of DefaultEventLoggingService.java
.
This simple implementation serialises the Event
passed to it to an XML String and passes that to an implementation of LogReceiver
.
By default this library will use the LoggerLogReceiver
implementation to receive and handle the XML events.
This implementation will write the XML to an SLF4J logger called event-logger
.
You then need to add configuration to your logging framework, i.e. Log4J/Logback/etc. to handle the event-logger
logs, e.g. writing them to rolled log files.
See here for an example Logback configuration.
Sending your rolled event logs to Stroom would then be done using something like send_to_stroom.sh or stroom-log-sender.
If you don't wish to use the LoggerLogReceiver
to handle the logs, you can make your own implementation of LogReceiver
.
In order to use your own implementaion you need to set the system property event.logging.logreceiver
with the fully qualified class name of your implementation.
LogReceiverFactory
will then issue new instances of your implementation instead of LoggerLogReceiver
.
Examples
Examples of how to construct various types of events and log them can be found in the test class base/src/test/java/event/logging/EventLoggingServiceIT.java
.
These examples assume you have created your own implementation of EventLoggingService that overrides the createEvent(...)
methods.
Using your own implemtation that extends DefaultEventLoggingService
is the preferred approach to dealing with common values in your events.
See CustomEventLoggingService for an example.
The following is a very simple example of logging an Authentication type event using the default logging service supplied with the API. This example does not use any common event values so
// Define your own implementation of EventLoggingService that provides common event values
public static class CustomEventLoggingService extends DefaultEventLoggingService {
@Override
public Event createEvent(final String typeId,
final String description,
final Purpose purpose,
final EventAction eventAction) {
return Event.builder()
.withEventTime(EventTime.builder()
.withTimeCreated(new Date())
.build())
.withEventSource(EventSource.builder()
.withSystem(SystemDetail.builder()
.withName("My System Name")
.withEnvironment("Test")
.withVersion(getBuildVersion())
.build())
.withGenerator("CustomEventLoggingService")
.withClient(getClientDevice())
.withDevice(getThisDevice())
.withUser(User.builder()
.withId(getLoggedInUserId())
.build())
.build())
.withEventDetail(EventDetail.builder()
.withTypeId(typeId)
.withDescription(description)
.withPurpose(purpose != null ? purpose : getSessionPurpose())
.withEventAction(eventAction)
.build())
.build();
}
}
// Create the logging service
final EventLoggingService eventLoggingService = new CustomEventLoggingService();
// Log some work that produces a result. This will make use of createEvent for common event values.
final UserAccount userAccount = eventLoggingService.loggedResult(
"LOGON",
"User " + userId + " logged on",
AuthenticateEventAction.builder()
.withAction(AuthenticateAction.LOGON)
.withUser(User.builder()
.withId(userId)
.build())
.build()
() -> {
// Perform authentication and logon
// An exception here will cause an unsuccessful logon event to be logged
// then the original exception will be re-thrown.
return account;
});
The various forms of the loggedXXX
methods provide the simplest means of logging an event and handle failure cases for you, setting Outcome/Success to false if an exception is thrown.
Some forms of loggedXXX
allow the loggedWork lambda to return a LoggedOutcome object to indicate success/failure rather than using exceptions.
There are also forms of these methods that allow you to change the logged EventAction based on the work being performed, e.g. if you need to add in the details of the results of a search.
For examples of the various forms see App.java
For instances where you want to handle failure conditions manually you can use this approach:
eventLoggingService.log(
"LogoffNowBanner",
"User shown logoff now banner",
ViewEventAction.builder()
.addBanner(Banner.builder()
.withMessage("The system is about to be shutdown for maintenance, log off now!")
.build())
.build());
When building the Event model you can use the fully fluent style (using the end()
methods on the Builder
classes) to build the event.
While more compact, this is heavily reliant on careful indentation to ensure readability.
// Create the event object
final Event event = Event.builder()
.withEventTime()
.withTimeCreated(new Date())
.end()
.withEventSource()
.withSystem()
.withName("Test System")
.withEnvironment("Test")
.end()
.withGenerator("JUnit")
.withDevice()
.withIPAddress("123.123.123.123")
.end()
.withUser()
.withId("someuser")
.end()
.end()
.withEventDetail()
.withTypeId("LOGON")
.withDescription("A user logon")
.withAuthenticate()
.withAction(AuthenticateAction.LOGON)
.withUser()
.withId("someuser")
.end()
.end()
.end()
.build();
event-logging is used by Stroom.
You can see how it is used in Stroom here: StroomEventLoggingServiceImpl
Upgrading from v3/4 to v5
v5 of this library introduces a number of breaking changes that will require you to alter your existing event creation code if migrating to v5. We appreciate that introducing breaking changes causes a lot of pain and we don't do it lightly. Unfortunately there were a number of fundamental issues with the previous versions of the library that made it difficult to work with.
Fluent Builders
event-logging v5 has introduced fluent style builder classes and methods for each class in the Event
model.
These make creating events or sub-trees within events much easier and neater.
There is however no requirement to change existing code to use the builders.
Moved Classes
For clarity all classes have been made top-level.
Previously a lot of the classes were declared as static inner classes.
Where this has happened, e.g. event.logging.Event.EventDetail
=> event.logging.EventDetail
, it should just be case of fixing the import or removing the extra parent class from the qualifier.
Renamed Classes
The following classes have been renamed:
Event.EventDetail.Alert
=>AlertEventAction
Event.EventDetail.Approval
=>ApprovalEventAction
Event.EventDetail.Authenticate
=>AuthenticateEventAction
Event.EventDetail.Authorise
=>AuthoriseEventAction
Event.EventDetail.Authorise
=>AuthoriseEventAction
CopyMove
=>CopyEventAction
&MoveEventAction
Export
=>ExportEventAction
Import
=>ImportEventAction
Install
=>InstallEventAction
&InstallEventAction
Event.EventDetail.Network
=>NetworkEventAction
NetworkSrcDst
=>NetworkLocation
NetworkSrcDstTrasnportProtocol
=>NetworkProtocol
NetworkSrcDstTrasnportProtocol
=>NetworkProtocol
Object
=>OtherObject
Event.EventDetail.Print
=>PrintEventAction
Event.EventDetail.Process
=>ProcessEventAction
SendReceive
=>SendEventAction
&ReceiveEventAction
Search
=>SearchEventAction
Unknown
=>UnknownEventAction
Event.EventDetail.Update
=>UpdateEventAction
Type changes
Some properties have had their classes changed for clarity:
ObjectOutcome Event.EventDetail.create
=>CreateEventAction EventDetail.create
ObjectOutcome Event.EventDetail.delete
=>DeleteEventAction EventDetail.delete
ObjectOutcome Event.EventDetail.search
=>SearchEventAction EventDetail.search
CopyMove Event.EventDetail.copy
=>CopyEventAction EventDetail.copy
CopyMove Event.EventDetail.move
=>MoveEventAction EventDetail.move
Install Event.EventDetail.uninstall
=>UninstallEventAction EventDetail.uninstall
ObjectOutcome Event.EventDetail.view
=>ViewEventAction EventDetail.view
Removed Classes
A number of deprecated classes and properties have been removed:
Event.EventDetail.antiMalware
AntiMalware
BaseAdvancedQueryItemComplexType
- replaced with marker interfaceAdvancedQueryItem
SearchResult
- useSearchResults
instead.Event.EventDetail.classification
- useEvent.classification
instead.From
New Interfaces
A number of new marker interfaces have been added to make the API clearer.
For example all the possible event actions (e.g. send, create, delete, etc.) under EventDetail
now implement the marker interface EventAction
.
Developing this library
The documention for developers contributing to this library see here.