The Tracking components in Slate Kit are useful for diagnostics, specifically for tracking, storing and viewing values and success/failure states. This are especially useful for jobs, reports, caching, and ETL (Extract, Transform, Load) operations. These are NOT a replacement for monitoring libraries like Micrometer.
Goal | Description |
1. Supplemental | Supplement the Results for tracking logical categories of errors. |
2. Inspection | Tracking and storing of complex objects/values for runtime level inspection |
3. Structured | Provides data structures to assist with structured logging and alerts |
This component is currently stable and can be used for both Android and Server
repositories {
// other repositories
maven { url "http://dl.bintray.com/codehelixinc/slatekit" }
}
dependencies {
// other dependencies ...
compile 'com.slatekit:slatekit-tracking:1.0.0'
}
Jar | slatekit.tracking.jar |
Package | slatekit.tracking |
Sources | slatekit-tracking |
Example | Example_Tracking.kt |
Version | |
License | |
Requires | See build.gradle for more info. |
The context can be constructed manually or using convenience methods that build the context from the command line args, and configs.
import slatekit.common.CommonContext
// Create simple context
val ctx1 = CommonContext.simple("demoapp")
Name | Description | More |
1. Calls | Tracks total processed, succeeded, failed operations. | more |
2. Counts | Tracks total successes and all error categories from Status | more |
3. Lasts | Tracks the last request and response/result values grouped by Status | more |
4. Event | Models a past event with fields and is used for structured logging. | more |
5. Events | Event handler for handling/tracking results based on Status | more |
6. Recorder | Records diagnostics via the calls, counts, events, logger components. | more |
7. Updates | Tracks an updating value by storing its create/update time, value and total changes. | more |
This is a simple counter to keep track of the total times an operation is called / passed / failed. These can be integrated with more comprehensive Timers/Guages from such libraries as Micrometer. See Calls for more info.
val id = SimpleIdentity("beta", "setup", Agent.Job, "dev")
val calls = Calls(id)
// Use case 1.1: Increment attempt
calls.inc()
// Use case 1.2: Increment number of passed calls
calls.passed()
// Use case 1.3: Increment number of failed calls
calls.failed()
// Use case 1.4: Get the total number of times the operation was run
calls.totalRuns()
// Use case 1.4a: Get the last error
calls.totalPassed()
// Use case 1.4b: Get the last error
calls.totalFailed()
// Use case 1.5: Determine if operation has run ( if totalPassed > 0 )
calls.hasRun()
// Use case 1.6: Get the last time the call was made
calls.lastTime()
// Use case 1.7: Get the last error
calls.lastError()
Counters count the totals across the logical errors groups from Status. These can be integrated with more comprehensive Timers/Guages from such libraries as Micrometer. See Counters for more info.
/**
* Slate Kit heavily uses logical success / failure categories @see[slatekit.results.Status]
* 1. Succeeded -> Success
* 2. Pending -> In Progress
* 3. Denied -> Security related
* 4. Ignored -> Ignored items
* 5. Invalid -> Invalid data
* 6. Errored -> Expected errors
* 7. Unexpected -> Unexpected errors
* This counters component keeps track of various categories
*/
val counts = Counters(id, custom = listOf("deferred"))
// Use case 2.1: Increment total processed
counts.incProcessed()
counts.incSucceeded()
counts.incDenied()
counts.incInvalid()
counts.incIgnored()
counts.incErrored()
counts.incUnexpected()
counts.inc("deferred")
// Use case 2.2: Increment total processed
counts.decProcessed()
counts.decSucceeded()
counts.decDenied()
counts.decInvalid()
counts.decIgnored()
counts.decErrored()
counts.decUnexpected()
counts.dec("deferred")
// Use case 2.3: Get totals
counts.totalProcessed()
counts.totalSucceeded()
counts.totalDenied()
counts.totalInvalid()
counts.totalIgnored()
counts.totalErrored()
counts.totalUnexpected()
counts.totalCustom("deferred")
Lasts track request and response/result values for inspecting values at runtime. These become useful during diagnosing specific requests/response that may match certain criteria and having access to the full object vs simply logging the data. See Lasts for more info.
// Lets setup some example request / result types
data class UserRequest(val id:String, val action:String)
data class UserResult (val id:String, val action:String, val msg:String)
val sampleRequest = UserRequest("uuid123", "register")
val sampleResult = UserResult("uuid123", "registration", "registered as beta user")
// Use case 3.1: Track various states
val lasts = Lasts<UserRequest, UserResult, Err>(Identity.test("job1"))
lasts.succeeded (this, sampleRequest, sampleResult)
lasts.denied (this, sampleRequest, Err.of("Not authorized"))
lasts.invalid (this, sampleRequest, Err.of("Not a beta user"))
lasts.ignored (this, sampleRequest, Err.of("In active user"))
lasts.errored (this, sampleRequest, Err.of("Unable to determine user type"))
lasts.unexpected(this, sampleRequest, Err.of("Unexpected error while handling analytics"))
// Use case 3.2: Get info
println(lasts.lastSuccess())
println(lasts.lastDenied())
println(lasts.lastInvalid())
println(lasts.lastIgnored())
println(lasts.lastErrored())
println(lasts.lastUnexpected())
// Use case 3.3: Clear the tracking
lasts.clear()
The event model is a general purpose data structure to represent a past event with extensible fields. This model can then be used for structured logging, alerts, and analytics. See Event for more info.
val event = Event(
area = "registration",
name = "NEW_ANDROID_REGISTRATION",
agent ="job",
env = "dev",
uuid = "abc-123-xyz",
desc = "User registration via mobile",
status= Codes.SUCCESS,
target= "registration-alerts",
tag = "a1b2c3",
fields= listOf(
Triple("region" , "usa" , ""),
Triple("device" , "android" , "")
)
)
You have have to the logs/factory to create loggers. See Events for more info.
// Lets setup some example request / result types
data class UserRequest(val id:String, val action:String)
data class UserResult (val id:String, val action:String, val msg:String)
val sampleRequest = UserRequest("uuid123", "register")
val sampleResult = UserResult("uuid123", "registration", "registered as beta user")
/** Setup a simple Events class that can handle Requests/Results/Failures of type
* request = UserRequest, result = UserResult, error = @see[slatekit.results.Err]
* This also needs to be able to convert the request / result / failure to an event.
*/
val events = Events<UserRequest, UserResult, Err>(
successConverter = { sender, req, res -> event.copy(uuid = res.id, desc = res.msg)},
failureConverter = { sender, req, err -> event.copy(uuid = req.id, desc = "Failed registering beta user", status = err.status)}
)
// Use case 4.2: Model a sample request and result using slatekit.results.Outcome and have the handler
// process it accordingly based on its category
events.handle(this, sampleRequest, Outcomes.success(sampleResult))
events.handle(this, sampleRequest, Outcomes.denied(Err.of("Not authorized")))
events.handle(this, sampleRequest, Outcomes.invalid(Err.of("Not a beta user")))
events.handle(this, sampleRequest, Outcomes.ignored(Err.of("In active user")))
events.handle(this, sampleRequest, Outcomes.errored(Err.of("Unable to determine user type")))
events.handle(this, sampleRequest, Outcomes.unexpected(Err.of("Unexpected error while handling analytics")))
The recorder component is a combination of all the above and can be used to record a request / result across calls, counts, lasts, events, logs. See Recorder for more info.
val recorder = Recorder<UserRequest, UserResult>(Identity.test("job1"), LoggerConsole(), calls, counts, lasts, events)
// Use case 5: Record the request / result in the recorder which will track the call, counts, lasts, and events
recorder.record(this, sampleRequest, Outcomes.success(sampleResult))
recorder.record(this, sampleRequest, Outcomes.denied(Err.of("Not authorized")))
recorder.record(this, sampleRequest, Outcomes.invalid(Err.of("Not a beta user")))
recorder.record(this, sampleRequest, Outcomes.ignored(Err.of("In active user")))
recorder.record(this, sampleRequest, Outcomes.errored(Err.of("Unable to determine user type")))
recorder.record(this, sampleRequest, Outcomes.unexpected(Err.of("Unexpected error while handling analytics")))
Updates track the initial creation time, update time, total updates of some value T. See Updates for more info.
// Initial settings
data class UserSettings(val userId:String, val isBetaTester:Boolean)
val settings = UserSettings("user1", false)
// Track as updates
val updates = Updates.of(settings)
val update1 = updates.set(settings)
val update2 = update1.map { it.copy(isBetaTester = true) }
println(update2.created) // initial creation time
println(update2.updated) // last update time
println(update2.applied) // total times changed
println(update2.current) // current value