In a sentence, ghostwriter-api is the bridge between the compile-time and the runtime part of Ghostwriter. It does the bridging using a set of callbacks that get executed for specific events triggered by your application, where the compile-time part makes sure that the callbacks are called at the right time (by instrumenting the API calls into your code), while the runtime part does the actual handling of the data.
The ghostwriter-api module does the loading of various handlers (runtime components) using a ServiceLoader. Meaning that if you put a specific runtime JAR on the classpath it will be loaded and used for handling the events.
Important: only single runtime module can be loaded.
There are situations, where relying on the classpath and ServiceLoader to use the correct runtime module is not enough. For example, you might be working on a web application where you want to be able to switch from one solution to another with the push of a button. Switching runtime modules is possible manually as well through the provided API.
io.ghostwriter.GhostWriter.setTracerProvider(...)
For example, for setting ghostwriter-tracer as the runtime handler run the following code:
final GhostWriterTracerProvider tracerProvider = new GhostWriterTracerProvider();
GhostWriter.setTracerProvider(tracerProvider);
The above example assumes that you have ghostwriter-api and ghostwriter-tracer JARs specified as dependencies of your application.
In case you want to get feedback about the module loading process of GhostWriter you need to enable the verbose logging by setting the GHOSTWRITER_VERBOSE environmental property to true.
export GHOSTWRITER_VERBOSE=true
After starting you application with the correct environmental value settings ghostwriter-api will output the logging information using JUL.
For creating your own runtime implementations, you need to implement the provided io.ghostwriter.Tracer interface, that provides callbacks for handling events, such as:
-
calling a function or procedure
-
assignment to a field or variable
-
returning a value from a function
-
exiting a function or procedure
-
unexpected error occurred
The instance of your handler is created through a factory that you need to provide by implementing the io.ghostwriter.TracerProvider interface.
For details, refer to the provided JavaDoc’s of the interfaces.
Before you start using your own runtime implementation, you need to package it into a standalone JAR, that conforms to the ServiceLoader requirements. A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services.
For a GhostWriter runtime implementation, this means having a file called io.ghostwriter.TracerProvider in your _src/main/META-INF/services folder, where the contents of the file correspond to the fully qualified name of your TracerProvider implementation.
For a working example, check one of the provided runtime implementations, such as the ghostwriter-tracer.