Written by

InterSystems Corporation
Article David Loveluck · Feb 25, 2019 4m read

Using Grafana directly from IRIS

There have been some very helpful articles in the community that show how to use Grafana with IRIS (or Cache/Ensemble) by using an intermediate database.

But I wanted to get at IRIS structures directly. In particular, i wanted to access the Cache History monitor data that is accessible by SQL as described here

https://community.intersystems.com/post/apm-using-cach%C3%A9-history-mo…

and didn't want anything between me and the data.

I already had class queries that returned the data i wanted, so i just needed to embed them in a REST class that returned JSON. I haven't included my class Grafana.MonitorData because it could be anything but i can if people want it.

There were only two difficult points. One was making sure i had local time and utc time worked out at each point. The other was that Grafana doesn't like values like .25 without the leading zero and gives javascript errors  - “t.dataList.map is not a function”. which is why i have the line with  $FN(tValue,,4).

I have simplified my production code to make the principals clear. I could put it on github but it is so simple, it doesn't really warrant that.


 

Spoiler

 

Class Grafana.SYSHistory Extends %CSP.REST
{
XData UrlMap
{
<Routes>
    <Route Url="/" Method="GET" Call="testAvailability" Cors="true" />
    <Route Url="/search" Method="POST" Call="metricFindQuery" Cors="true" />
    <Route Url="/query" Method="POST" Call="query" Cors="true" />
    </Routes>
}
ClassMethod testAvailability() As %Status
{
    write "ok"
    quit $$$OK
}
/// This method returns list of available metrics.
ClassMethod metricFindQuery() As %Status
{
    do ##class(Grafana.MonitorData).GetSupportedMetrics(.metrics)
    "["
    set sub=""
    set firsttime=1
    do 
      set sub=$o(metrics(sub))
      quit:sub=""
    if firsttime=0 ","
     set firsttime=0
     """",sub,"""" 
while sub'=""
write "]"
quit $$$OK
}
/// Data format for Grafana - http://docs.grafana.org/plugins/developing/datasources/
ClassMethod query() As %Status
{
set obj = {}.%FromJSON(%request.Content)
 if obj="" {
 write "no object found"
 quit $$$OK   
    
   }
    
    set iter=obj.targets.%GetIterator()
    set tMetrics=0
    while iter.%GetNext(.key,.value) {
    set tMetrics=tMetrics+1
    set tMetrics(tMetrics) = value.target
    }
    set from = obj.range.from
    set to = obj.range.to
#define classname 1
#define queryname 2
set (className,queryName)=""
//hard code the class and use 'NamedQuery' items so we don't allow any access to any data via any query...
set className="Grafana.MonitorData"
set queryName="SysMonHistorySummary"
write "["
for i=1:1:tMetrics {
if i>1 ","
"{""target"":"""_tMetrics(i)_""",""datapoints"":["
do ..ExportJSON(className,queryName,from,to,tMetrics(i))
write "]}"
}
write "]"
quit $$$OK
}
/// The className and QueryName determing the query to be executed.
/// from and to are local time in %Date (a.k.k. $horolog) format.
/// The query must return a value for the metric. This code assumes the values are returned
/// as Avg_Metric and RunDate, but you could change that


ClassMethod ExportJSON(className As %String, queryName As %String, from, to, pMetric As %String) As %Status
{
if className="" quit $$$OK
if queryName="" quit $$$OK
set rs=##class(%ResultSet).%New(className_":"_queryName)
if rs="" quit $$$ERROR($$$QueryDoesNotExist,className_":"_queryName)
// use this just for param info
set sc=$classmethod(className,queryName_"GetInfo",.colinfo,.paraminfo,.idinfo,.QHandle,0,.extinfo)
  
//The request must contain data with names matching the parameters of the query.
//Convert date and time parameters from strings to $h
set from=$e(from,1,19)
set to=$e(to,1,19)
set RunDateUTCFromH=$zdth(from,3)
set RunDateFromH=$zdth(RunDateUTCFromH,-3)
set RunDateUTCToH=$zdth(to,3)
set RunDateToH=$zdth(RunDateUTCToH,-3)
set tSc=rs.Execute(RunDateFromH,RunDateToH,"live",pMetric) //param(1),param(2))
if $$$ISERR(tSc) quit tSc
set rowcnt=0
while rs.Next() {
set rowcnt=rowcnt+1
if rowcnt>1 write ","
write "["
set tRunDate=rs.Data("RunDate")
set tUtcRunDate=$zdt(tRunDate,-3)
set tValue=rs.Data("Avg_Metric")
set tPosixTime=##class(%Library.PosixTime).OdbcToLogical($zdt(tUtcRunDate,3,3))
set tUnixTime=##class(%Library.PosixTime).LogicalToUnixTime(tPosixTime)_"000"
write $fn(tValue,,4),",",tUnixTime
write "]"
}
quit $$$OK
}
}

Comments

Arto Alatalo · Feb 8, 2020

David, thanks for sharing your work.

Does your system work reliable and your approach can be taken into real use? What about performance? I have same intentions: to use Grafana directly with Cache.

0
Dmitry Maslennikov · Mar 29, 2021

Hi @David Loveluck, and anybody who wanted it.

Have a look at one of my latest projects. I did Grafana plugin for IRIS, which can connect and gather any data directly. It's in active development and will be extended with much more possibilities very soon. And I'm going to publish it on Grafana Plugins list as well, for easier installation. Stay tuned, and do not hesitate to contact me directly or through issues in the repository, if you have some advice, what would you like to see there first.

0