Dashboards & Visualizations

Issue with css, html, js Formatting Search Output

genesiusj
Builder

Hello,
I'm starting this post with a shoutout to @niketnilay because I used one of his post as the basis for my dashboard. However, input from any/all Splunkers out there is appreciated.

Here is my code, modified from niketnilay's post. How to show table result in one page/ table modification

   <row>
    <panel>
      <title>Show Table Results in One Page</title>
      <table>
        <search>
          <query> index=oit_printer_monitoring AND type=Printer 
    | eval timeConvDate=strftime(_time,"%a %m-%d-%Y") 
    | eval timeConvTime=strftime(_time,"%H:%M:%S") 
    | eval statusNum=case(status="printing,deleting,error",4,status="error,toner low",4,status="printing,error",4,status="paper jam",4,status="no toner",4,status="error,offline",4,status="error",4,
        status="door open,error",3,status="spooling,paused",3,status="paused",3,status="out of paper",3,status="error,out of paper",3,status="offline",3,status="door open",3,
        status="toner low",2,status="restarted",2,
        status="printing,deleting",1,status="printed,deleting",1,status="printing,printed,deleting",1,status="error,warming up",1,status="spooling,printing",1,status="warming up",1,status="spooling",1,status="printing",1,status="normal",1) 
    | sort - statusNum, status 
    | fields printer, status, statusNum, timeConvDate, timeConvTime 
    | dedup printer 
    | eval printer="#".printer 
    | eval component=mvzip(printer,mvzip(status,mvzip(timeConvDate,timeConvTime,"..."),"..."),"...") 
    | stats values(component) as component 
    | nomv component 
    | eval component="<div>".replace(component,"#","</div><div>")."</div>" 
    | makemv delim="..." component 
    | table component</query>......

Explanation.
Create two new fields for time and date. | eval timeConvDate=strftime(_time,"%a %m-%d-%Y") | eval timeConvTime=strftime(_time,"%H:%M:%S")
Create a new field to represent the status as a number that can be sorted later. | eval statusNum=case(status="printing,deleting,error",4,status="error,toner low",4,....
Append a "#" in front of my first field, printer, to establish the beginning of a new field component. | eval printer="#".printer
The field **component
* will be concatenation of several other fields: printer, status, and the timeConvDate and timeConvTime fields as one. | eval component=mvzip(printer,mvzip(status,mvzip(timeConvDate,timeConvTime,"..."),"..."),"...") I used "..." instead of "|" between each of the concatenated fields.
The next two lines are from niketnilay's code unchanged. | stats values(component) as component | nomv component '
Modified niketnilay's eval/replace code as follows:
| eval component="<div>".replace(component,"#","</div><div>")."</div>"By using the "..." all 4 fields stay associated to each other, and the "#" indicates of the start of the field **component**.
Replaced the "..." within the field component with a carriage return/newline.
| makemv delim="..." component `

Before continuing, I have a question about the field named *component. When I substituted ***component* with my field name, printerResult, it did not work. I had substituted in the js code as well. Therefore, I used component instead. My question is, in the js code, the word Components is used (plural and upper case C). Is this a special variable name for js and that is why it was used?*

I am using display_token_with_html_content.js as written.

 require([
     "jquery",
     "splunkjs/mvc",
     "splunkjs/mvc/simplexml/ready!"
 ], function (
     $,
     mvc
 ) {
         var defaultTokenModel = mvc.Components.get("default");
         defaultTokenModel.on("change:tokResultsInHTML", function (model, tokResultsInHTML, options) {
             if (tokResultsInHTML !== undefined) {
                 $("#htmlTokenContainer").html(tokResultsInHTML);
             }
         });
     });

I used niketnilay's css in a panel

<panel>
  <title>Applying HTML Style</title>
  <html>
     <style>
       #htmlTokenContainer{
         display:flex;
         flex-wrap:wrap;
       }
       #htmlTokenContainer div{
         width: 260px;
       }
     </style>
     $tokResultsInHTML$
   </html>....

Lastly, I created another panel with just the token value assigned earlier per the niketnilay example.

<panel>
  <title>Format Data as desired output and show as html</title>
  <html>       
       <div id="htmlTokenContainer">      
       </div>
     </html>....

Here is a screenshot of my results.

Printer Dashboard Issue

What I am trying to accomplish is placing each value of component into individual cells. The formatting would be all centered. As the final requirement click the printer name in any one of the cells would set a token, which displays another panel (depends="token" ) and populates the panel with data specific to the printer clicked.

Thanks and God bless,
Genesius

0 Karma
1 Solution

niketnilay
Legend

@genesiusj @vinothn

Turns out this is purely Simple XML problem (with CSS override if required). No <html> table and JS Extension required.

I used the following approach:

  1. Build a Splunk table with Multi-value cells (You can arrange as single Row or single column). For each Printer multiple values are Printer Name, Status, Status Type, Date and Time.
  2. Apply color to table cell using expression color palette based on regex match for Status Type. Since case() is not available nested if() can be applied for setting more than two color ranges. Refer to answer. Refer to older answer: https://answers.splunk.com/answers/668588/how-to-add-custom-color-to-blank-cell.html
  3. Enable table drilldown. $click.value$ gives all values in the multi-value cell clicked with each value separated by comma and $click.value2$ gives specific value in the multi-valued cell clicked.
  4. Apply custom CSS to format Table cell look as a tile as per the requirement.

alt text

Following is the Simple XML code required.
PS: Supports drilldown and requires no JS. Also supports sorting by Printer Name by default table sorting property 🙂

 <dashboard>
  <label>Table with Color and Format</label>
  <row>
    <panel>
      <html>
        <style>
          #table_tile table tbody{
            display:flex;
            flex-wrap: wrap;
          }
          #table_tile table tbody tr{
            margin-right:10px;
            margin-bottom:10px;
          }
          #table_tile table tbody tr td{
            width: 180px;
            text-align: center;
          }
        </style>
        <div>
          <div>Clicked Value in Cell (click.value):$click.value$</div>
          <div>Clicked Cell Values (click.value2):$click.value2$</div>
        </div>
      </html>
      <table id="table_tile">
        <search>
          <query>| makeresults 
| eval status="printing,deleting,error;printing,error;door open,error;restarted;printed,deleting;error,offline;error,offline;spooling,paused" 
| makemv status delim=";" 
| mvexpand status 
| eval delta=500 
| streamstats count as sno 
| eval printer="printer".sno 
| eval delta=delta*sno 
| eval _time=_time-delta 
| fields - delta sno 
| eval timeConvDate=strftime(_time,"%a %m-%d-%Y") 
| eval timeConvTime=strftime(_time,"%H:%M:%S") 
| eval statusClass=case(status="printing,deleting,error","status_fatal",status="error,toner low","status_fatal",status="printing,error","status_fatal",status="paper jam","status_fatal",status="no toner","status_fatal",status="error,offline","status_fatal",status="error","status_fatal",
    status="door open,error","status_critical",status="spooling,paused","status_critical",status="paused","status_critical",status="out of paper","status_critical",status="error,out of paper","status_critical",status="offline","status_critical",status="door open","status_critical",
    status="toner low","status_low",status="restarted","status_low",
    status="printing,deleting","status_info",status="printed,deleting","status_info",status="printing,printed,deleting","status_info",status="error,warming up","status_info",status="spooling,printing","status_info",status="error,offline","status_info",status="spooling","status_info",status="printing","status_info",status="normal","status_info") 
| sort - statusClass, status 
| table printer, status, statusClass, timeConvDate, timeConvTime
| eval "Printer Info"=printer."###".status."###".statusClass."###".timeConvDate."###".timeConvTime
| fields "Printer Info"
| makemv "Printer Info" delim="###"</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
          <sampleRatio>1</sampleRatio>
        </search>
        <option name="count">20</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">cell</option>
        <option name="percentagesRow">false</option>
        <option name="refresh.display">progressbar</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
        <format type="color" field="Printer Info">
          <colorPalette type="expression">if (match(value,"status_fatal"), "#DC4E41", if(match(value,"^status_critical"),"#F8BE34", if(match(value,"status_low"),"#62B3B2","#B6C75A")))</colorPalette>
        </format>
        <drilldown>
          <set token="click.value">$click.value$</set>
          <set token="click.value2">$click.value2$</set>
        </drilldown>
      </table>
    </panel>
  </row>
</dashboard>
____________________________________________
| makeresults | eval message= "Happy Splunking!!!"

View solution in original post

vinothn
Path Finder

@niketnilay i gone through the link it was helpful thanks for the assistance, but still i want to know how to set a token and provide a link to another page on a single click.
ie:- when i am clicking on the panel it should set a token as well redirect to different dashboard.

0 Karma

niketnilay
Legend

@vinothn if you want the JS based approach I would suggest you to read jQuery Selector and jQuery Click event coding on W3Schools which should be hardly 10 min each.

If you are not well versed with JS/jQuery and want SimpleXML based approach, refer to other two answers (one with Unicode character has code as screenshot try this second after you have tried one without Unicode). The Simple XML code also displays how you can set token. $click.value$ is used to access all values in the multivalue cell as comma separated values (you can use <eval> to set token using split() and mvindex() chained eval functions).

If you are only interested in specific value clicked you can try $click.value2$. The example has a run anywhere Simple XML code which you can copy paste and test. Please try out and confirm.

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
0 Karma

niketnilay
Legend

@vinothn refer to the links posted on accepted answer to understand the jQuery and CSS Selectors and also jQuery event handling and basic of JavaScript.

Nevertheless, following are the changes you need to make:

  1. Change CSS Selector <style>, so that Mouse Cursor changes to clickable hand icon when the mouse hovers over the Tile header Printer Column.

      #html_table div.html_table_column div.html_column_printer{
        cursor: pointer;
      }
    
  2. Use the same CSS selector to code the on("click","",function(){ }); click event handler.
    PS: I have used console.log and alert section for debug/test purpose. Use your redirect code instead.

     // Handle Click on the HTML Table Tile printer column
     $(document).on("click","#html_table div.html_table_column div.html_column_printer",function(){
        //Get the value Printer Column and print in console log.
        var strClickedPrinter=$(this).text();
        console.log("Clicked printer: ",strClickedPrinter);
        //Perform redirection only for Printer4
        if(strClickedPrinter=="printer4"){
            //Redirection Code goes here
            alert("Printer4 Clicked Open New Window.");
        }
     });
    

    Please try out and confirm. Do up vote the comments/answers if they helped.

Happy Learning! and Happy Splunking!

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
0 Karma

genesiusj
Builder

@niketnilay
We are unable to run the above dashboard. Receive Unquoted attribute error on line 22.
| eval htmlData="<tr class=\"html_table_row\">"."<td class=\"html_table_column\" id=\"column_printer\">".printer."</td>"."<td class=\"html_table_column\" id=\"column_status\">".status."</td>"."<td class=\"html_table_column\" id=\"column_statusNum\">".statusNum."</td>"."<td class=\"html_table_column\" id=\"column_timeConvDate\">".timeConvDate."</td>"."<td class=\"html_table_column\" id=\"column_timeConvTime\">".timeConvTime."</td>"."<tr>"

We counted the quotes, and they're an even number, and appear to be in the proper positions.

As compared to the previous example with section (where request was for single field to be displayed in four columns), I have created with row and cell .

But IS what we are looking for. The only difference is a newline separating the four fields - printer, status, date, time - but in the same cell.

We'll be continuing to investigate further. Please let me know if our requirements are unclear.

Thanks and God bless,
Genesius

0 Karma

niketnilay
Legend

@genesiusj less than and greater than sign needs to be escaped in Dashboard code. On answers it resolves to the actual character hence you are not seeing & lt ; or & gt ; (purposely added space so that they do not get replaced on Answer).

Refer to w3schools link for using Espace sequence for the less than and greater than characters and replace the same in your dashboard code... https://www.w3schools.com/html/html_entities.asp

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
0 Karma

genesiusj
Builder

Thanks @niketnilay
DOH! Silly mistake, that I am usually good at catching.
I will correct and test.
God bless,
Genesius

0 Karma

genesiusj
Builder

@niketnilay
Making the <> change worked.

As compared to the previous example with section (where request was for single field to be displayed in four columns), I have created with row and cell.

Not sure if I need a combination of div and table?

What I need is to now format these 4 fields (printer, status, timeConvDate, and timeConvTime) into a single field (printerResults) with a newline after the printer, status, and timeConvDate values. The statusNum field is using strictly for sorting purposes and will not be displayed.
Next, I need to color the entire cell based on the statusNum.
Lastly, a drill-down (opening a new panel at the top of this dashboard) when the cell is clicked (or just the printer if the code will be too difficult) that will display these four fields, plus additional fields for contact and location information, as well as additional drill-down links.

BTW, I am searching for CSS/HTML/JS for dummies type of sites so I can learn. I worked with CSS and HTML a very long time ago, but never with JS.

Thank you again for all your help and insights.

God bless,
Genesius

0 Karma

niketnilay
Legend

Can you whiteboard your output? Even pen and paper output with some example is fine.

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
0 Karma

genesiusj
Builder

@niketnilay
Will do.

Thanks and God bless,
Genesius

0 Karma

genesiusj
Builder

@niketnilay
For whatever reason, the links to the screenshots keeps failing.
Examples: https://imgur.com/lDMRScj
Required Display: https://imgur.com/23pRnKq

The first image is a screenshot of the output I am receiving. The code will follow each example.
The second image is a mockup (used Excel) of our requirements.

When a printer is clicked, drill-down panels #4 & #5 should be displayed. Panel #4 contains the info as the previous clicked panel; plus other fields (not shown here) for contact and location information. Panel #5 contains a jpg of the printer. Clicking on the jpg will result in a new browser window opening. This window is the web interface for the printer.

THIS IS THE BASE SEARCH CODE

index=oit_printer_monitoring AND type=Printer
  | eval timeConvDate=strftime(_time,"%a %m-%d-%Y")
  | eval timeConvTime=strftime(_time,"%H:%M:%S")
  | eval statusNum=case(status IN ("printing,deleting,error","error,toner low","printing,error","paper jam","no toner","error,offline","error"),4,
       status IN ("door open,error","spooling,paused","paused","out of paper","error,out of paper","offline","door open"),3,
       status IN ("toner low","restarted"),2,
       status IN ("printing,deleting","printed,deleting","printing,printed,deleting","error,warming up","spooling,printing","warming up","spooling","printing","normal"),1) 
  | sort - statusNum, status 
  | fields  printer, status, statusNum, timeConvDate, timeConvTime

Panel #1: the data is displayed in a similar fashion to that from the code you provided. All the fields are clickable, and the drill-down will open panels #4 & #5. However, the data for a single printer is displayed across 4 columns; and formatting based on the statusNum field effects only the status field.

  <row>
    <panel>
      <title>Regular Table - Formatting Works - Click Any Field</title>
      <html depends="$alwaysHideCSSStyle$"/>
      <table>
        <search base="baseSearch">
          <query>
| eval printerResult=mvzip(printer,mvzip(status,mvzip(timeConvDate,timeConvTime,"  "),"  "),"  ")
| makemv delim="  " printerResult
| dedup printer
| table printer, status, statusNum, timeConvDate, timeConvTime</query>
        </search>
        <option name="count">100</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">cell</option>
        <option name="percentagesRow">false</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
        <option name="showPager">true</option>
        <format type="color" field="status">
          <colorPalette type="expression">if(like(value,"%out of paper%"),"#ee0000",if(like(value,"%toner low%"),"#ff9b0","#27db17"))</colorPalette>
        </format>
        <drilldown>
          <set token="tok_printerInfo">$click.value$</set>
        </drilldown>
      </table>
    </panel>

Panel #2: the data displayed is a single cell per printer (status, date, and time), and the background color has been formatted according to the statusNum field. However, only the printer field is clickable (drill-down will open panels #4 & #5); and the data is displayed in a single column.

    <panel>
      <title>Multivalue 1 Row - Formatting Works - Click Printer Name Only</title>
      <html depends="$alwaysHideCSSStyle$"/>
      <table>
        <search base="baseSearch">
          <query>
| eval printerResult=mvzip(printer,mvzip(status,mvzip(timeConvDate,timeConvTime,"  "),"  "),"  ")
| makemv delim="  " printerResult
| dedup printer
| table printerResult, printer, status, statusNum, timeConvDate, timeConvTime
| fields - column, - printer, - status, - statusNum, - timeConvDate, - timeConvTime</query>
        </search>
        <option name="count">100</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">cell</option>
        <option name="percentagesRow">false</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
        <option name="showPager">true</option>
        <format type="color" field="printerResult">
          <colorPalette type="expression">if(like(value,"%out of paper%"),"#ee0000",if(like(value,"%toner low%"),"#ff9b0","#27db17"))</colorPalette>
        </format>
        <drilldown>
          <set token="tok_printerInfo">$click.value2$</set>
        </drilldown>
      </table>
    </panel>

Panel #3: the data is displayed in a single cell per printer (status, date, and time), and is displayed horizontally. However, only the printer field is clickable (drill-down will open panels #4 & #5); no color formatting is run based on the statusNum field, and the data is displayed in a single row.

    <panel>
      <title>Transpose 1 Row - Not Formatting - Click Printer Name Only</title>
      <html depends="$alwaysHideCSSStyle$"/>
      <table>
        <search base="baseSearch">
          <query>
| eval printerResult=mvzip(printer,mvzip(status,mvzip(timeConvDate,timeConvTime,"  "),"  "),"  ")
| makemv delim="  " printerResult
| dedup printer
| table printerResult
| transpose 0
| fields - column</query>
        </search>
        <option name="count">100</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">cell</option>
        <option name="percentagesRow">false</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
        <option name="showPager">false</option>
        <format type="color" field="printerResult">
          <colorPalette type="expression">if(like(value,"%out of paper%"),"#ee0000",if(like(value,"%toner low%"),"#ff9b0","#27db17"))</colorPalette>
        </format>
        <drilldown>
          <set token="tok_printerInfo">$click.value2$</set>
        </drilldown>
      </table>
    </panel>

All panels use this table formatting (color formatting is in the panel code).

  <row>
    <panel depends="$alwaysHideCSS$">
      <html>
        <style>
          thead{
            font-size: 13pt !important;
            display: none !important;
            text-align: center !important;
          }
            table, tbody, tr, td{
              font-size: 10pt !important;
              font-color: 0xffffff !important;
              text-align: center !important;
              border: 3px solid white;
          }
            .dashboard-row .dashboard-panel .panel-element-row {
              overflow: auto;
              max-height: 300px;
          }
        </style>
      </html>
    </panel>
  </row>

This is the code for panels #4 & #5.

  <row depends="$tok_printerInfo$">
    <panel>
      <title>Drill-down Panel 1</title>
      <table>
        <search base="baseSearch">
          <query>
| where printer="$tok_printerInfo$"
| table printer, status, timeConvDate, timeConvTime
| dedup printer
| transpose 0 
<!--| transpose 0 header_field=printer-->
| fields - column</query>
        </search>
        <option name="count">100</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">none</option>
        <option name="percentagesRow">false</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
      </table>
    </panel>
    <panel>
      <title>Drill-down Panel 2</title>
      <html>
    <center>
      <a href="http://$tok_printerInfo$.web.us" target="_blank">        <img src="/static/app/search/images/$tok_printerInfo$.jpg" height="auto" width="150px"/>      </a>
      </center>
  </html>
    </panel>
  </row>

Here are our requirements.
1. Single cell per printer (printer, status, date, and time fields).
2. The formatting of the cell is based on the value of the statusNum field.
3. Entire cell clickable, not just the printer field. Not a deal-breaker.
4. Table format (see screenshot below): left to right, across row (this would be based on other search/sort criteria not mentioned here for simplicity). When reaching the end of the visible page, a new row is displayed (this is why I was using your post on How to show table result in one page/ table modification). This continues until all printers are displayed. It is OK to have a vertical scroll bar, but not a horizontal one.

Thanks and God bless,
Genesius

0 Karma

niketnilay
Legend

@genesiusj I have updated my answer. Refer to screenshot and new code. Please try out and confirm. You may have to change SPL and CSS as per your needs.

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
0 Karma

genesiusj
Builder

@niketnilay
THANK YOU SO MUCH!
I copied the XML as is, and I have the same results as your screenshot.
I'm going to modify your code using my data sources and get back to you.
Thanks and God bless,
Genesius

Did you miss .conf21 Virtual?

Good news! The event's keynotes and many of its breakout sessions are now available online, and still totally FREE!