Custom Charts with Flat Data
The following step-by-step instructions can help you build a custom chart that expects flat data.
Use Case
Build a custom heatmap chart to understand which organization and search type combinations generate the most query load.
Before You Begin
To build a custom chart, you need to bridge two things:
- What data you already have: This is the structure of your ingested data, which is usually flat.
- What the chart expects: Each chart type needs data in a specific format. Some charts expect flat data, while others require nested data.
Note: Understanding both is important because it helps you write the right SQL query, prepare the data through grouping or aggregation, reshape the results to match the chart’s structure, and map them correctly in the JavaScript code that renders the chart.
Step 1: Understand the Ingested Dataset
In OpenObserve, the data ingested into a stream is typically in a flat structure.
Example: In the following dataset, each row represents a single event or query log with its own timestamp, organization ID, search type, and query duration.
Note: Use the Logs page to view the data ingested to the stream.
Step 2: Identify the Expected Data Structure
Before moving ahead, identify what structure the chart expects. The heatmap chart expects flat data.
In this example, each row in data[0] must contain:
organization_id
(example: "test")search_type
(example: "logs")- A numeric value (
total_seconds
) representing total query time.
Note: For charts that expect flat data, reshaping is not needed. SQL alone is enough to prepare the data in required format.
Step 3: Prepare the Data (via SQL)
In the Add Panel page, under Fields, select the desired stream type and stream name.
Build a SQL query in the Query Editor to fetch and prepare the data:
- Converting
query_duration
(text) into numeric seconds - Grouping by
organization_id
andsearch_type
- Summing the durations to get a meaningful value per cell in the heatmap
Select a time range to fetch the relevant dataset for your chart.
Expected Query Result
Note: OpenObserve stores the result of the query in the data
object as an array of an array.
Step 4: Inspect the Queried Dataset
Inspect the queried dataset:
Step 5: JavaScript Code to Render the Heatmap
In the JavaScript editor, you must construct an object named option
.
This option
object defines how the chart looks and behaves. To feed data into the chart, use the query result stored in data[0]
The following script:
- Extracts all unique orgs and search types
- Creates a [x, y, value] format for each cell
- Configures a color gradient using visualMap
Step 6: View Result
Click Apply to generate the chart.
Understand the Chart
In the chart,
- The
x-axis
shows search_type (logs, alerts, dashboards). - The
y-axis
showsorganization_id
(test, production). - Each cell color reflects total query time.
Use this heatmap to quickly spot load-heavy combinations like production → alerts
, where optimization or alert review may be needed.
Troubleshoot
Use the following guidance to identify and fix common issues when working with custom charts:
1. No data found
Cause: The SQL query did not return any results.
Fix:
- Confirm the selected time range includes data.
- Check that the stream name and field names are correct.
- Run the query in the Logs view to verify that results are returned.
2. Uncaught ReferenceError: option is not defined
Cause: The chart configuration was not defined correctly.
Fix:
- Ensure
option = {...}
is defined at the end of your JavaScript. - Do not rename the option variable or wrap it in a function or condition.
3. Unsafe code detected: Invalid JavaScript syntax.
Cause: There is a syntax error in your JavaScript.
Fix:
- Check for missing or extra commas, brackets, or quotation marks.
- Open your browser's developer console to locate the error.
- Use
console.log()
to test your script step by step.
4. Chart Not Rendering:
Cause: The query returned data, but the chart did not render.
Fix:
- Use
console.log(data[0])
to confirm that the dataset is not empty. - Make sure the field names in your script match the query output.