Skip to content

Instantly share code, notes, and snippets.

@thinkbeforecoding
Created September 7, 2016 13:26

Revisions

  1. thinkbeforecoding created this gist Sep 7, 2016.
    86 changes: 86 additions & 0 deletions LogaryMetrics.fs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    open Hopac
    open Hopac.Infixes
    open NodaTime
    open Logary
    open Logary.Metrics.Reservoirs
    open Logary.Configuration
    open Logary.Targets
    open Metric

    module PointName =
    let append suffix (PointName name) = Array.append name [| suffix |] |> PointName.ofArray

    type Timing =
    {
    source: (Value*Units) Stream.Src
    }

    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module Timing =
    let create() = { source = Stream.Src.create() }
    let update timing v = Stream.Src.value timing.source v
    let report timing (x,v) = update timing v >>-. x
    let reportAsync timing result = async.Bind(result, Job.toAsync << report timing)
    let metric timing name =
    let minName = name |> PointName.append "min"
    let maxName = name |> PointName.append "max"
    let medianName = name |> PointName.append "median"
    let countName = name |> PointName.append "count"
    let upper95 = name |> PointName.append "upper_95"

    let reduce state = function
    | Float v, Seconds ->
    int64 (v * 1000.) :: state
    | _ -> state

    let tick state =
    let snap = Metrics.Reservoirs.Snapshot.create (Array.ofList state)
    [], [ snap |> Snapshot.size |> int64 |> Int64 |> Message.gauge countName
    snap |> Snapshot.median |> int64 |> Int64 |> Message.gauge medianName
    snap |> Snapshot.min |> Int64 |> Message.gauge minName
    snap |> Snapshot.max |> Int64 |> Message.gauge maxName
    snap |> Snapshot.percentile95th |> int64 |> Int64|> Message.gauge upper95 ]

    job {
    let! metric = Metric.create reduce [] tick
    do! metric |> Metric.consume (Stream.Src.tap timing.source)
    return metric }

    let time f =
    let sw = System.Diagnostics.Stopwatch.StartNew()
    f(), (Float sw.Elapsed.TotalSeconds, Units.Seconds)

    let timeAsync f =
    async {
    let sw = System.Diagnostics.Stopwatch.StartNew()
    let! result = f
    return result, (Float sw.Elapsed.TotalSeconds, Units.Seconds)
    }

    // usage sample

    let sampleTiming = Timing.create() // this defines a timing stream source

    let doSomething() =
    printfn "Do something"

    let logary =
    withLogaryManager "SampleApp" (
    withTargets [
    Console.create (Console.empty) "console"
    Graphite.create { Graphite.GraphiteConf.hostname = "graphite-server"; port = 2003us} "graphite"
    ]
    >> withMetrics [
    MetricConf.create (Duration.FromSeconds 10L) "test.logary.timing" (Timing.metric sampleTiming)
    ]
    >> withRules [
    Rule.createForTarget "console"
    Rule.createForTarget "graphite"
    ]
    ) |> run

    doSomething
    |> time
    |> Timing.report sampleTiming
    |> run