Play Events composes various logging and metric standards into traits which can be mixed in to create events for your play app.
Auditing, Metrics, Alerts or Logging data can be recorded. A default monitoring implementation for HTTP errors is also provided.
##Download play-events
resolvers += Resolver.bintrayRepo("hmrc", "releases")
libraryDependencies += "uk.gov.hmrc" %% "play-events" % "x.x.x"
##Creating an Alert Event
A basic Alert Event would look like this:
package uk.gov.hmrc.play.events.examples
import uk.gov.hmrc.play.events.AlertLevel._
import uk.gov.hmrc.play.events.Alertable
import uk.gov.hmrc.play.events.AlertCode
case class ExampleAlertEvent(source: String,
name: String,
level: AlertLevel,
alertCode: AlertCode) extends Alertable
object ExampleAlertEvent {
def apply() = new ExampleAlertEvent(
source = "TestApp",
name = "External API Alert",
level = CRITICAL,
alertCode = "EG-A"
)
}
##Recording an Alert Event
The above event would be recorded in your app by mixing in the DefaultEventRecorder
trait and calling:
record(ExampleAlertEvent())
Alert Events are written out to the logs in the following standard format. Match on this format with your Paging or Alerts service.
Logger.warn(s"alert:${alertable.level}:source:${alertable.source}" +
":code:${alertable.alertCode}:name:${alertable.name}")
So the output of this alert will be similar to
2015-08-04 13:54:56,793 level=[WARN] logger=[application] message=[alert:CRITICAL:source:TestApp:code:EG-A:name:External Api Alert]
##Creating an Audit Event
Audit Events look the same as any other event except they have privateData
in place of data
.
Use this paramater to store sensitive data which should be written out to a secure events log.
They also typically take in an implicit HeaderCarrier
in order to log the user session.
package uk.gov.hmrc.play.events.examples
import uk.gov.hmrc.play.http.HeaderCarrier
import uk.gov.hmrc.play.events.Auditable
case class ExampleAuditEvent(source: String,
name: String,
tags: Map[String, String],
privateData: Map[String, String]) extends Auditable
object ExampleAuditEvent {
def apply(testCount: Int, testName: String)(implicit hc: HeaderCarrier) =
ExampleAuditEvent(
source = "example-source",
name = "test-conducted",
tags = Map(hc.toAuditTags("testConducted",
"/your-web-app/example-path/").toSeq: _*),
privateData = hc.toAuditDetails() ++ buildAuditData(testCount, testName)
)
private def buildAuditData(count: Int, name: String) = {
Map(
"Test Name" -> name.toString,
"Tests Run" -> count.toString
)
}
}
##Recording an Audit Event
Audit Events require a AuditConnector
to be set up inside your application to register events with.
This should be registered inside a AuditEventHandler
.
import uk.gov.hmrc.play.audit.http.connector.AuditConnector
import uk.gov.hmrc.play.events.handlers.AuditEventHandler
object DefaultAuditEventHandler extends AuditEventHandler {
override val auditConnector: AuditConnector = AuditConnector
}
You then add this Handler to your own Recorder
which you then include in your app where ever you want to audit.
Note that we include super.eventHandlers
so that the Default Alert, Metric and Loggable Handlers are included.
trait ExampleEventRecorder extends DefaultEventRecorder {
override def eventHandlers: Set[EventHandler] =
super.eventHandlers ++ Set(DefaultAuditEventHandler)
}
And now you record the Audit Event as normal
record(ExampleAuditEvent(5, "Example"))
##Creating a Metric Event
A Metric Event currently writes out to the log in a standard format, which can be analysed in your Log Digestion app to create reports etc.
package uk.gov.hmrc.play.events.examples
import uk.gov.hmrc.play.events.Measurable
case class ExampleMetricEvent(source: String,
name: String,
data: Map[String, String]) extends Measurable
object ExampleMetricEvent {
def apply(fileId: String, fileType: String) =
new ExampleMetricEvent(
source = "TestApp",
name = "NumberOfCreatedFilings",
data = Map (
"File ID" -> fileId,
"File Type" -> fileType
))
}
##Recording a Metric Event
The DefaultEventRecorder
trait writes out Metric Events in this standard format.
Logger.info(s"metric:source:${measurable.source}:name:${measurable.name}" +
":data:${measurable.data}")
They can be recorded by:
record(ExampleMetricEvent("1234567", "SHARED"))
The output of this metric will be similar to
2015-08-04 13:54:56,793 level=[INFO] logger=[application] message=[metric:source:TestApp:name:NumberOfCreatedFilings:data:Map(File ID -> 1234567, File Type -> SHARED]
##Combining Event Types in One Event
The event traits can be mixed in together to allow for an event to be recorded once but produce multiple types of events in your logs.
Note the separation between privateData
and data
for AuditEvents. This also uses the Loggable
trait to
add an additional message into the logs.
package uk.gov.hmrc.play.events.examples
import uk.gov.hmrc.play.http.HeaderCarrier
import uk.gov.hmrc.play.events.AlertLevel.AlertLevel
import uk.gov.hmrc.play.events._
import uk.gov.hmrc.play.events.AlertCode
case class ExampleCombinedEvent(source: String,
name: String,
tags: Map[String, String],
privateData: Map[String, String],
data: Map[String, String],
level: AlertLevel,
alertCode: AlertCode)
extends Auditable with Measurable with Loggable with Alertable {
override def log = "Combined Event occurred"
}
object ExampleCombinedEvent {
def apply(filingID: String, otherFilingInfo: String, userPassword: String, alertCode: AlertCode)
(implicit hc: HeaderCarrier) =
new ExampleCombinedEvent(
source = "test-app",
name = "CombinedEvent",
tags = Map(hc.toAuditTags("testConducted",
"/your-web-app/example-path/").toSeq: _*),
privateData = Map("Password" -> userPassword) ++
generateData(filingID, otherFilingInfo),
data = hc.toAuditDetails() ++ generateData(filingID, otherFilingInfo),
alertCode = alertCode,
AlertLevel.WARNING
)
def generateData(filingID: String, otherFilingInfo: String): Map[String, String] =
Map("Filing ID" -> filingID, "Filing Info" -> otherFilingInfo)
}
This event can be recorded using the ExampleEventRecorder
above.
##Default Monitoring of Http Errors and Error Counts
The traits HttpErrorMonitor
and HttpErrorCountMonitor
provide a default monitoring solution for any app that uses the uk.gov.hmrc.http-verbs
library. In any class that extends HttpErrorMonitor
or HttpErrorCountMonitor
you can wrap your code with monitor
as follows:
def getHttpData()(implicit hc: HeaderCarrier) : Future[ExampleHttpResponse] = {
monitor() {
http.GET[ExampleHttpResponse](exampleGetUrl)
}
}
The default value for AlertCode
is "Unknown".
Or you can provide an alert code:
def getHttpData()(implicit hc: HeaderCarrier) : Future[ExampleHttpResponse] = {
monitor("ALERT-CODE") {
http.GET[ExampleHttpResponse](exampleGetUrl)
}
}
This will catch any of uk.gov.hmrc.play.http.{HttpException, Upstream4xxResponse, Upstream5xxResponse}
, log events if an exception occurs, and pass the exception along.
The traits HttpErrorMonitor
and HttpErrorCountMonitor
are stackable - the events logged as a result of wrapping your code in monitor
depend upon whether you've mixed in the traits HttpErrorMonitor
or HttpErrorCountMonitor
or both, as follows:
HttpErrorMonitor
logs both a Metric and Critical Alert event for each exceptionHttpErrorCountMonitor
logs a Metric event for each exception
##Default Monitoring of Http Response Times
In any class that extends the Timer
trait you can wrap a Http call with timer
. This can be combined with the Http error monitoring described in the previous section, as follows:
def getHttpData()(implicit hc: HeaderCarrier) : Future[ExampleHttpResponse] = {
monitor("ALERT-CODE") {
timer("ALERT-CODE") {
http.GET[ExampleHttpResponse](exampleGetUrl)
}
}
}
As for Http error monitoring, you can provide an AlertCode
, as in the example above, or you can use the default value, which is "Unknown".
This will log a Metric event containing the time in nanoseconds taken to make the Http call.
##Custom Monitoring of Http Errors, Error Counts and Response Times
Examples are included of how to extend the HttpErrorMonitor
, HttpErrorCountMonitor
and Timer
traits, which is useful for integrating with other Metrics and Alerts libraries.
Your classes can extend these custom monitoring traits and wrap your code with monitor
or timer
as appropriate.
The following shows how to create a custom Timer
with a custom event and custom DefaultEventRecorder
.
import uk.gov.hmrc.play.events._
import uk.gov.hmrc.play.events.handlers.{DefaultAlertEventHandler, DefaultMetricsEventHandler, EventHandler}
import uk.gov.hmrc.play.events.monitoring._
import scala.concurrent.duration.Duration
trait ExampleEventRecorder extends DefaultEventRecorder {
override def eventHandlers: Set[EventHandler] = Set(DefaultMetricsEventHandler, DefaultAlertEventHandler)
}
trait ExampleTimer extends Timer with ExampleEventRecorder {
override val source = "TestApp"
override def createTimerEvent(alertCode: AlertCode, duration: Duration): Measurable = ExampleTimerEvent(alertCode, duration)
}
case class ExampleTimerEvent(alertCode: String, duration: Duration) extends Measurable {
override val source = "TestApp"
override def data: Map[String, String] = Map (
"Time" -> s"${duration.length}"
)
override def name: String = s"Timer-$alertCode"
}
##License
This code is open source software licensed under the Apache 2.0 License.