App

Overview

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.



Goals

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


Status

This component is currently stable and can be used for both Android and Server


Back to top



Install

    repositories {
        // other repositories
        maven { url  "http://dl.bintray.com/codehelixinc/slatekit" }
    }

    dependencies {
        // other dependencies ...

        compile 'com.slatekit:slatekit-tracking:1.0.0'
    }
package slatekit.tracking
jar slatekit.tracking.jar
git slatekit/src/lib/kotlin/slatekit-tracking
docs Tracking
uses slatekit.results, slatekit.common
license Apache 2.0
example Example_Tracking.kt

Back to top



Requires

This component uses several components from the Slate Kit Utilities.

Component Description
Slate Kit - Results To model successes and failures with optional status codes
Slate Kit - Common Common utilities for both android + server


Back to top



Sample

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")


Back to top



Guide


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

Back to top



Calls

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()
         
Back to features Back to top


Counts

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")
     
Back to features Back to top


Lasts

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()
    
Back to features Back to top


Event

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" , "")
         )
    )
    
Back to features Back to top


Events

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")))

    
Back to features Back to top


Recorder

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")))
     
Back to features Back to top


Updates

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
     
Back to features Back to top



Back to top