What if you have a series that is reporting the state of a service at intervals?
Something like
timestamp status
1465577250 up
1465577370 down
1465577490 down
1465577610 down
1465577730 down
1465577850 up
1465577970 up
1465578090 down
1465578210 down
You would like to trim down that series into the edges of the event, meaning when the status changed.
timestamp status
1465577250 up
1465577370 down
1465577850 up
1465578090 down
I see two ways of doing this:
.stateChangesOnly property. Example via method 1
|from()...
|alert()
.info(lambda: "status" == 'up')
.warn(lambda: "status" == 'down')
.fieldLevel('level')
.stateChangesOnly()
|elapsed('level', 1s)
// Going to get elapsed time between up->down events and down->up events
// we only care about down->up events aka how long it was down.
|where(lambda:"level" == 'down')
...
The above script will work in current versions, but it seems odd and is limited to only tracking 4 states, OK, INFO, WARN, CRIT, and the state names have to change to those names.
Example via option 2
|from()...
|eval(lambda: "p.status" != "status")
.previous('p.')
.as('changed')
.keep() // Keep will keep the current fields so the stats of `down`, means we just changed to the down state.
// Drop all points where the status didn't change
|where(lambda: "changed")
|elapsed('status', 1s)
// Going to get elapsed time between up->down events and down->up events
// we only care about down->up events aka how long it was down.
|where(lambda:"status" == 'down')
...
This example reads much better as its clear what we are trying to do instead of abusing the alert node. This scales out to as many states as you like and preserves the original state names.
See relevant mailing list discussion https://groups.google.com/forum/?pli=1#!topic/influxdb/GspSPy0_REE
This also has a use in online incremental algorithms, where you want to calculate the next value in a series from the previous value.
Can this help to build a counter ? Example: One transition from ON to OFF where keep on adding the count as+1 also determining the time elapsed between ON and OFF states.
Hi Nathanielc
you have mentioned on how o read previous status to get the right elapsed time, when I'm trying, i'm getting error that no "previous method is found"
Can you please help me, Im stuck from the last 3 days.
here is my code
var e10_1 = stream
|from()
.database('telegraf10')
.retentionPolicy('autogen')
.measurement('downtime')
|eval(lambda: "p.value" != "value")
.previous('p.')
.as('changed')
.keep()
|where(lambda: "changed")
|elapsed('value' , 1s)
|where(lambda: "value" == 1) // 1:error
Rgds
Pankaj
We need exactly this feature.
The case is a sensor delivering data from plants in horticultures and other places.
The moisture goes up and down following irrigation and natural rain.
To calculate irrigation events we look at the moisture growth/decline and from the derivative we can spot irrigation.
This script below gives us exactly the state sequence as @nathanielc described above.
Trimming down the series would be a really welcomed feature. We also need to calculate the elapsed times between the events but guess this can be done already with a elapsed node .
stream
|from()
.database('spiio_senors')
.retentionPolicy('autogen')
.measurement('sensor_data')
.groupBy(*)
|window()
.period(30m)
.every(30m)
.align()
|derivative('moisture')
.as('dryrate')
|eval(lambda: ceil("dryrate"))
.as('irrigation_state')
|influxDBOut()
.database('irrigation')
.retentionPolicy('autogen')
.measurement('irrigations')
.precision('s’)
👍 for the feature.
For something like status with known states there are some workarounds as @nathanielc mentioned. However we are trying to detect changes in a string field with arbitrary values. I modified the example:
timestamp myfield
1465577250 blabla1
1465577370 blabla2
1465577490 blabla2
1465577610 blabla2
1465577730 blabla2
1465577850 blabla3
1465577970 blabla3
1465578090 blabla4
1465578210 blabla4
We would like to be alerted whenever myfield value changes.
timestamp myfield
1465577250 blabla1
1465577370 blabla2
1465577850 blabla3
1465578090 blabla4
I guess the script should look like this:
var message = 'Value changed to {{ index .Fields "value" }}.'
var data = stream
|from()
.database(db)
.retentionPolicy(rp)
.measurement(measurement)
.groupBy(groupBy)
.where(whereFilter)
|eval(lambda: "myfield")
.as('value')
var trigger = data
|alert()
.valueChange("value")
.message(message)
.post(url)
Any help would be great.
We can do it easily with the derivative.
Here's a example of an on-line threshold crossing that only outputs changes :
|from()...
|eval(lambda: int("value" > threshold))
.as('outofboundary')
.keep('outofboundary')
|derivative('outofboundary')
.as('diff')
|where(lambda: "diff" != 0) // filter out successive same-state points
|eval(lambda: if("diff" > 0, 'going-above-threshold', 'back-to-normal'))
.as('change')
.keep('change')
|alert()...
Thank you for your response. I guess derivative would solve the problem for numeric fields, but the problem stays the same for fields with string type. Correct me if I am wrong. But I think derivative cannot be applied to non-integer values. What can we do if the field is of type string?
Fixed by #1844
Most helpful comment
We can do it easily with the
derivative.Here's a example of an on-line threshold crossing that only outputs changes :