log-viewer is a minimalistic scala-application for the command-line that can be used to view the
content of an Eventuate event log. It connects remotely to a running
Eventuate-based application to retrieve
DurableEvent
s
of a given span of sequence numbers and simply prints a string representation to stdout.
There are several options on how to start and customize the log-viewer. See below for more advanced alternatives, if the one proposed here is not suitable for you.
-
Download the universal zip-artifact.
-
Unzip anywhere creating a directory
log-viewer-<version>
. -
Drop jar-files of your application containing
- the application specific classes of the event payloads,
- the corresponding custom serializers and
- the corresponding akka configuration for custom serializers in a
reference.conf
in
log-viewer-<version>/ext
-
Start log-viewer through the script in
log-viewer-<version>/bin
:In Unix environments:
log-viewer --help
to display the help page about the command line options.
log-viewer --batchSize 20 --fromSeqNo 150 --maxEvents 100 --remoteHost foo.example.com --remotePort 5555
to display 100 events from sequence number 150 on of the
default
log of the application running on host foo.example.com with a akka remote port of 5555 (configured through the configuration variableakka.remote.netty.tcp.port
of the Eventuate application)
If the application jars do not contain a reference.conf
with the akka configuration for custom serializers
you can provide a corresponding file on command line as follows:
log-viewer -Dconfig.file=path/serializer.conf ...
log-viewer comes with a usage page when called with command line option --help
.
log-viewer allows to specify a format string for printing retrieved events in a desired
format. There are actually two types of template engines that can be selected by the command
line option --eventFormatter
:
-
CaseClass
: Allows to specify a simplejava.util.Formatter
-like template to select specific fields of aDurableEvent
. It does not support filtering nor selecting details of the payload so fomatting is limitted to the direct fields of anDurableEvent
. Check--help
for more details. -
Velocity
: Allows to specify a Velocity template. As Velocity supports conditional expressions this can also be used to filter events and display only those that match a certain criterion. The velocity template has access to two variables:ev
referencing theDurableEvent
nl
referencing a line separator
The following exmaple template assumes the payload class:
case class CustomEvent(a:Int, b:String)
With this a filtering template could be defined as:
#if($ev.payload().a() > 10)$ev.payload().b()$nl#end
This would display only
b
of events witha > 10
. Note the$nl
within the if expression at the end of the template string. Without it all events would be printed in a single line. If it were outside of the if expression a filtered event would still be displayed as an empty line.
When you start log-viewer through sbt (i.e. from the source-tree with sbt log-viewer/run
), you have two options to customize the classpath:
- Modify
build.sbt
and include your dependencies. - Drop the jar-files containing your classes in the
lib
folder. As thelib
folder is explicitly excluded from git-management, this will keep your working directory clean.
If the customized classpath does not contain a reference.conf
file containing the
configuration for the custom serializers, you can provide a corresponding file through the system property
config.file
. You have once again two options for this:
- Add
javaOptions += "-Dconfig.file=..."
tobuild.sbt
. - Call sbt with an additional first argument:
sbt 'set javaOptions += "-Dconfig.file=..."' "log-viewer/run ..."
.
log-viewer uses sbt-native-packager for packaging
the application into a distributable artifact. You can for example use sbt universal:packageBin
to
create a zip-file containing the application. This universal artifact contains a bin
folder with
scripts for starting the application (log-viewer
) and a lib
folder with all required jars and an empty ext
folder.
To install the application, you can unzip the archive anywhere.
To create a custom log-viewer package that already contains all class definitions and configuration required for deserializing application specific events, you can create a new sbt-project with dependencies to your application specific classes as well as the log-viewer project:
libraryDependencies ++= Seq(
"com.rbmhtechnology.eventuate-tools" %% "log-viewer" % "<version>"
// your dependencies ...
)
// for snapshots
resolvers += "OJO Snapshots" at "https://oss.jfrog.org/oss-snapshot-local"
// for releases
resolvers += "OJO Releases" at "https://oss.jfrog.org/oss-release-local"
and use sbt-native-packager in a similar manner as log-viewer does:
enablePlugins(JavaAppPackaging)
publishArtifact in (Compile, packageDoc) := false
publishArtifact in (Test, packageDoc) := false
makeDeploymentSettings(Universal, packageBin in Universal, "zip")
publish <<= publish dependsOn (publish in Universal)
publishLocal <<= publishLocal dependsOn (publishLocal in Universal)
If the log-viewer cannot connect to the remote application (even if it is up and running) it runs into an ask timeout like follows:
akka.pattern.AskTimeoutException: Ask timed out on [ActorSelection[Anchor(akka.tcp://location@localhost:2552/), Path(/user/acceptor)]] after [15000 ms]. Sender[null] sent message of type "com.rbmhtechnology.eventuate.ReplicationProtocol$GetReplicationEndpointInfo$".
This is typically caused by wrong network configuration. The remote host (--remoteHost
) must be
the identical name or IP that is used by the remote application's remoting config
(akka.remote.netty.tcp.hostname
). If for example the remote application uses 127.0.0.1
log-viewer cannot connect with -rh localhost
. In that case you should see in the log-file of
the remote application something like the following:
10:45:20.089 [location-akka.actor.default-dispatcher-27] ERROR akka.remote.EndpointWriter - dropping message [class akka.actor.ActorSelectionMessage] for non-local recipient [Actor[akka.tcp://location@localhost:2552/]] arriving at [akka.tcp://location@localhost:2552] inbound addresses are [akka.tcp://[email protected]:2552]
10:45:34.955 [location-akka.actor.default-dispatcher-26] ERROR akka.remote.EndpointWriter - AssociationError [akka.tcp://[email protected]:2552] <- [akka.tcp://[email protected]:50550]: Error [Shut down address: akka.tcp://[email protected]:50550] [
akka.remote.ShutDownAssociation: Shut down address: akka.tcp://[email protected]:50550
Caused by: akka.remote.transport.Transport$InvalidAssociationException: The remote system terminated the association because it is shutting down.
]
The check for the name is even case sensitive!
The local bind address (--localBindAddress
) must be accessible for the remote host. If you
specify for example the name of your local host the remote application must be able to resolve this
name.
- log-viewer needs to have all application specific class definitions of payload instances including their custom serializers and corresponding configuration in its classpath. So you need to customize it before you can run it.
- The application whose event log is viewed must be up and running.
- The case class based event formatting (
--eventFormatter CaseClass
) implementation relies on the fact that theproductIterator
of acase class
instance returns the values in the same order asgetDeclaredFields
of the correspondingClass
. This is the case for scala 2.11 and JDK 1.8, however it is not guaranteed.
log-viewer uses the
ReplicationProtocol
to communicate with the running Eventuate-based application, so it basically looks like just another replication-client.
That is why it needs to have access to application jars.