Compare commits

...

15 Commits

Author SHA1 Message Date
Naman Verma
070b4b7061 chore: test file with way more examples 2026-03-18 22:01:16 +05:30
Naman Verma
7f4c06edd6 chore: test file with way more examples 2026-03-18 22:01:10 +05:30
Naman Verma
6bed20b5b9 fix: remove fields from variable specs that are there in ListVariable 2026-03-18 19:19:00 +05:30
Naman Verma
033bd3c9b8 feat: validation script 2026-03-18 15:52:05 +05:30
Naman Verma
d4c9a923fd chore: no commons (for now) 2026-03-18 15:28:35 +05:30
Naman Verma
387dcb529f chore: no config folder 2026-03-18 15:04:16 +05:30
Naman Verma
7a4da7bcc5 chore: no config folder 2026-03-18 15:04:02 +05:30
Naman Verma
b152fae3fa chore: remove validate file 2026-03-18 15:03:19 +05:30
Naman Verma
2ed766726c chore: remove manually written manifest and package 2026-03-18 15:02:23 +05:30
Naman Verma
8767f6a57d chore: remove stub for time series chart 2026-03-18 15:01:58 +05:30
Naman Verma
22d8c7599b chore: rm comment 2026-03-18 13:10:19 +05:30
Naman Verma
1019264272 chore: no need for PageSize type in commons, only used once 2026-03-18 13:07:31 +05:30
Naman Verma
c950d7e784 chore: no need for Signal type in commons, only used once 2026-03-18 13:05:32 +05:30
Naman Verma
1e279e6193 Merge branch 'main' into nv/4172 2026-03-18 13:03:09 +05:30
Naman Verma
d3a278c43e docs: perses schema for dashboards 2026-03-17 14:56:07 +05:30
31 changed files with 3094 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
module: "github.com/signoz/signoz/schemas"
language: {
version: "v0.12.0"
}

4
perses/deletePostValidate.sh Executable file
View File

@@ -0,0 +1,4 @@
docker rm -f perses
rm mf-manifest.json
rm package.json
rm signoz-0.0.1.tar.gz

1701
perses/examples/current.json Normal file

File diff suppressed because it is too large Load Diff

699
perses/examples/perses.json Normal file
View File

@@ -0,0 +1,699 @@
{
"kind": "Dashboard",
"metadata": {
"name": "the-everything-dashboard",
"project": "signoz"
},
"spec": {
"display": {
"name": "The everything dashboard",
"description": "Trying to cover as many concepts here as possible"
},
"duration": "1h",
"variables": [
{
"kind": "ListVariable",
"spec": {
"name": "serviceName",
"display": {
"name": "serviceName"
},
"allowAllValue": true,
"allowMultiple": false,
"plugin": {
"kind": "SigNozDynamicVariable",
"spec": {
"dynamicVariablesAttribute": "service.name",
"dynamicVariablesSource": "Metrics",
"sort": "DISABLED"
}
}
}
},
{
"kind": "ListVariable",
"spec": {
"name": "statusCodesFromQuery",
"display": {
"name": "statusCodesFromQuery"
},
"allowAllValue": true,
"allowMultiple": true,
"plugin": {
"kind": "SigNozQueryVariable",
"spec": {
"queryValue": "SELECT JSONExtractString(labels, 'http.status_code') AS status_code FROM signoz_metrics.distributed_time_series_v4_1day WHERE status_code != '' GROUP BY status_code",
"sort": "ASC"
}
}
}
},
{
"kind": "ListVariable",
"spec": {
"name": "limit",
"display": {
"name": "limit"
},
"allowAllValue": false,
"allowMultiple": false,
"plugin": {
"kind": "SigNozCustomVariable",
"spec": {
"customValue": "1,10,20,40,80,160,200",
"sort": "DISABLED"
}
}
}
}
],
"panels": {
"24e2697b": {
"kind": "Panel",
"spec": {
"display": {
"name": "total resp size"
},
"plugin": {
"kind": "TimeSeriesChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "http.server.response.body.size.sum",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "rate"
}
],
"filter": {
"expression": "http.response.status_code IN $statusCodesFromQuery"
},
"groupBy": [
{
"dataType": "string",
"id": "service.name--string--tag",
"isColumn": false,
"isJSON": false,
"key": "service.name",
"type": "tag"
}
],
"order": [],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"ff2f72f1": {
"kind": "Panel",
"spec": {
"display": {
"name": "fraction of calls"
},
"plugin": {
"kind": "TimeSeriesChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozCompositeQuery",
"spec": {
"queries": [
{
"type": "builder_query",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "signoz_calls_total",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "rate"
}
],
"filter": {
"expression": "service.name IN $serviceName AND http.status_code IN $statusCodesFromQuery"
},
"groupBy": [],
"order": [],
"disabled": true,
"legend": "",
"having": {
"expression": ""
}
}
},
{
"type": "builder_query",
"spec": {
"name": "B",
"signal": "metrics",
"expression": "B",
"aggregations": [
{
"metricName": "signoz_calls_total",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "rate"
}
],
"filter": {
"expression": "service.name in $serviceName"
},
"groupBy": [],
"order": [],
"disabled": true,
"legend": "",
"having": {
"expression": ""
}
}
},
{
"type": "builder_formula",
"spec": {
"name": "F1",
"expression": "A / B",
"legend": ""
}
}
]
}
}
}
}
]
}
},
"011605e7": {
"kind": "Panel",
"spec": {
"display": {
"name": "total resp size"
},
"plugin": {
"kind": "BarChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "http.server.response.body.size.sum",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "rate"
}
],
"filter": {
"expression": "http.response.status_code IN $statusCodesFromQuery"
},
"groupBy": [
{
"dataType": "string",
"id": "service.name--string--tag",
"isColumn": false,
"isJSON": false,
"key": "service.name",
"type": "tag"
}
],
"order": [],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"e23516fc": {
"kind": "Panel",
"spec": {
"display": {
"name": "num traces for service"
},
"plugin": {
"kind": "StatChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "traces",
"expression": "A",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "service.name = $serviceName "
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"130c8d6b": {
"kind": "Panel",
"spec": {
"display": {
"name": "num logs for service"
},
"plugin": {
"kind": "StatChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "logs",
"expression": "A",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "service.name = $serviceName "
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"246f7c6d": {
"kind": "Panel",
"spec": {
"display": {
"name": "num traces for service per resp code"
},
"plugin": {
"kind": "PieChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "traces",
"expression": "A",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "service.name = $serviceName isEntryPoint = 'true'"
},
"groupBy": [
{
"dataType": "float64",
"id": "http.response.status_code--float64--tag",
"isColumn": false,
"isJSON": false,
"key": "http.response.status_code",
"type": "tag"
}
],
"order": [],
"disabled": false,
"legend": "\"{{http.response.status_code}}\"",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"21f7d4d0": {
"kind": "Panel",
"spec": {
"display": {
"name": "average latency per service"
},
"plugin": {
"kind": "Table",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozClickHouseSQL",
"spec": {
"name": "A",
"query": "WITH\n __spatial_aggregation_cte AS\n (\n SELECT\n toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalSecond(60)) AS ts,\n `service.name`,\n le,\n sum(value) / 60 AS value\n FROM signoz_metrics.distributed_samples_v4 AS points\n INNER JOIN\n (\n SELECT\n fingerprint,\n JSONExtractString(labels, 'service.name') AS `service.name`,\n JSONExtractString(labels, 'le') AS le\n FROM signoz_metrics.time_series_v4\n WHERE (metric_name IN ('signoz_latency.bucket')) AND (LOWER(temporality) LIKE LOWER('delta')) AND (__normalized = 0)\n GROUP BY\n fingerprint,\n `service.name`,\n le\n ) AS filtered_time_series ON points.fingerprint = filtered_time_series.fingerprint\n WHERE metric_name IN ('signoz_latency.bucket')\n GROUP BY\n ts,\n `service.name`,\n le\n ),\n __histogramCTE AS\n (\n SELECT\n ts,\n `service.name`,\n histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.9) AS value\n FROM __spatial_aggregation_cte\n GROUP BY\n `service.name`,\n ts\n ORDER BY\n `service.name` ASC,\n ts ASC\n )\nSELECT\n `service.name` AS service,\n avg(value) AS avgLatency\nFROM __histogramCTE\nGROUP BY `service.name`",
"disabled": false,
"legend": ""
}
}
}
}
]
}
},
"ad5fd556": {
"kind": "Panel",
"spec": {
"display": {
"name": "logs from service"
},
"plugin": {
"kind": "LogsTable",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "logs",
"expression": "A",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "service.name = $serviceName"
},
"groupBy": [],
"order": [
{
"columnName": "timestamp",
"order": "desc"
},
{
"columnName": "id",
"order": "desc"
}
],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"f07b59ee": {
"kind": "Panel",
"spec": {
"display": {
"name": "response size buckets"
},
"plugin": {
"kind": "HistogramChart",
"spec": {
"bucketCount": 60,
"bucketWidth": 0,
"mergeAllActiveQueries": false
}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "http.server.response.body.size.bucket",
"reduceTo": "avg",
"spaceAggregation": "p90",
"timeAggregation": "rate"
}
],
"filter": {
"expression": ""
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "",
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"e1a41831": {
"kind": "Panel",
"spec": {
"display": {
"name": "trace operator"
},
"plugin": {
"kind": "TimeSeriesChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "traces",
"expression": "A",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "service.name = 'sampleapp-gateway' "
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "Gateway",
"having": {
"expression": ""
}
}
}
}
},
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "B",
"signal": "traces",
"expression": "B",
"aggregations": [
{
"expression": "count() "
}
],
"filter": {
"expression": "http.response.status_code = 200"
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "$serviceName",
"having": {
"expression": ""
}
}
}
}
}
]
}
}
},
"layouts": [
{
"kind": "Grid",
"spec": {
"items": [
{
"x": 0,
"y": 0,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/24e2697b"
}
},
{
"x": 6,
"y": 0,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/ff2f72f1"
}
},
{
"x": 0,
"y": 6,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/011605e7"
}
},
{
"x": 6,
"y": 6,
"width": 6,
"height": 3,
"content": {
"$ref": "#/spec/panels/e23516fc"
}
},
{
"x": 6,
"y": 9,
"width": 6,
"height": 3,
"content": {
"$ref": "#/spec/panels/130c8d6b"
}
},
{
"x": 0,
"y": 12,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/246f7c6d"
}
},
{
"x": 6,
"y": 12,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/21f7d4d0"
}
},
{
"x": 0,
"y": 18,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/ad5fd556"
}
},
{
"x": 6,
"y": 18,
"width": 6,
"height": 6,
"content": {
"$ref": "#/spec/panels/f07b59ee"
}
},
{
"x": 0,
"y": 24,
"width": 12,
"height": 6,
"content": {
"$ref": "#/spec/panels/e1a41831"
}
}
]
}
}
]
}
}

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""
Generates package.json and mf-manifest.json for a Perses plugin module
by reading the kind: "..." declarations from your CUE schema files,
then builds the archive ready to mount into Perses.
Usage (run from the root of your plugin folder, where schemas/ lives):
python3 generate_manifests.py --org signoz --name signoz --version 0.0.1
"""
import os
import re
import json
import tarfile
import argparse
# Maps keywords in schema folder names to Perses plugin kinds.
# Add more here if you introduce new plugin types.
KIND_HINTS = {
"datasource": "Datasource",
"variable": "Variable",
"promql": "TimeSeriesQuery",
"formula": "TimeSeriesQuery",
"join": "TimeSeriesQuery",
"sql": "TimeSeriesQuery",
"composite": "TimeSeriesQuery",
"builder": "TimeSeriesQuery",
"query": "TimeSeriesQuery",
"panel": "Panel",
"trace": "TraceQuery",
"log": "LogQuery",
"profile": "ProfileQuery",
}
def infer_kind(folder_name):
lower = folder_name.lower()
for hint, kind in KIND_HINTS.items():
if hint in lower:
return kind
return None
def extract_kind_from_cue(cue_file):
"""Extract kind: "PluginName" from a CUE file."""
with open(cue_file) as f:
content = f.read()
match = re.search(r'^\s*kind:\s*"([^"]+)"', content, re.MULTILINE)
return match.group(1) if match else None
def to_display_name(name):
"""Convert CamelCase to 'Camel Case'."""
return re.sub(r'(?<=[a-z])(?=[A-Z])', ' ', name)
def main():
parser = argparse.ArgumentParser(description="Generate Perses plugin manifests from CUE files and build archive.")
parser.add_argument("--org", required=True, help="Your org name, e.g. signoz")
parser.add_argument("--name", required=True, help="Plugin module name, e.g. signoz")
parser.add_argument("--version", default="0.0.1", help="Plugin version, e.g. 0.0.1")
parser.add_argument("--schemas-dir", default="schemas", help="Path to schemas directory (default: schemas)")
args = parser.parse_args()
schemas_dir = args.schemas_dir
if not os.path.isdir(schemas_dir):
print(f"Error: schemas directory '{schemas_dir}' not found. Run this script from your plugin root folder.")
exit(1)
plugins = []
for folder in sorted(os.listdir(schemas_dir)):
folder_path = os.path.join(schemas_dir, folder)
if not os.path.isdir(folder_path):
continue
plugin_kind_name = None
cue_file = os.path.join(folder_path, f"{folder}.cue")
if os.path.isfile(cue_file):
plugin_kind_name = extract_kind_from_cue(cue_file)
perses_kind = infer_kind(folder)
if not plugin_kind_name:
print(f"Warning: could not extract kind from '{cue_file}', skipping.")
continue
if not perses_kind:
print(f"Warning: could not infer Perses kind for folder '{folder}', skipping. Add a hint to KIND_HINTS.")
continue
plugins.append({
"kind": perses_kind,
"spec": {
"display": {"name": to_display_name(plugin_kind_name)},
"name": plugin_kind_name
}
})
print(f"Found: {plugin_kind_name} -> {perses_kind}")
if not plugins:
print("No plugins found. Check that your schemas directory contains CUE files with kind: declarations.")
exit(1)
# Generate mf-manifest.json
manifest = {
"id": args.name,
"name": args.name,
"metaData": {
"buildInfo": {
"buildVersion": args.version
}
},
"plugins": [
{"kind": p["kind"], "name": p["spec"]["name"]}
for p in plugins
]
}
# Generate package.json
package = {
"name": f"@{args.org}/{args.name}",
"version": args.version,
"description": f"{args.name} plugin module for Perses",
"perses": {
"schemasPath": "schemas",
"plugins": plugins
}
}
with open("mf-manifest.json", "w") as f:
json.dump(manifest, f, indent=2)
print("\nWrote mf-manifest.json")
with open("package.json", "w") as f:
json.dump(package, f, indent=2)
print("Wrote package.json")
# Build the archive
archive_name = f"{args.name}-{args.version}.tar.gz"
with tarfile.open(archive_name, "w:gz") as tar:
tar.add("package.json")
tar.add("mf-manifest.json")
if os.path.isdir("schemas"):
tar.add("schemas")
if os.path.isdir("cue.mod"):
tar.add("cue.mod")
print(f"Wrote {archive_name}")
print(f"\nDone! {len(plugins)} plugin(s) packaged:")
for p in plugins:
print(f" - {p['spec']['name']} ({p['kind']})")
if __name__ == "__main__":
main()

27
perses/panels/NOTE.txt Normal file
View File

@@ -0,0 +1,27 @@
Panel Plugins
All SigNoz panels use Perses built-in panel kinds directly. No custom CUE
schemas exist yet because Perses does not publish CUE schemas for its panel
plugins (only Go SDK + TypeScript). Once it does, each panel can embed the
upstream spec and add the SigNoz-specific fields listed below.
SigNoz-specific fields by panel type:
TimeSeriesChart timePreference
StatChart timePreference, contextLinks
BarChart timePreference, contextLinks
PieChart timePreference, contextLinks
Table timePreference, contextLinks
LogsTable (List) timePreference, selectedLogFields, selectedTracesFields, columnWidths
TraceTable timePreference, selectedTracesFields, columnWidths
HistogramChart timePreference, contextLinks, bucketCount, bucketWidth, mergeAllActiveQueries
Common fields:
timePreference — panel-local vs dashboard-global time range
contextLinks — clickable drill-down links on data points
Panel-specific fields:
selectedLogFields / selectedTracesFields — which fields to display as columns in list views
columnWidths — saved column width overrides
bucketCount / bucketWidth — histogram bucket configuration
mergeAllActiveQueries — combine multiple queries into one histogram

View File

@@ -0,0 +1,84 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/builder_query.go — QueryBuilderQuery
kind: "SigNozBuilderQuery"
spec: close({
name: #QueryName
signal: "metrics" | "logs" | "traces"
expression: string
disabled?: bool | *false
// Metrics use structured aggregations; logs/traces use expression-based.
aggregations?: [...#MetricAggregation]
expressionAggregations?: [...#ExpressionAggregation]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
order?: [...#OrderByItem]
selectFields?: [...]
limit?: #Limit
limitBy?: #LimitBy
offset?: #Offset
cursor?: string
having?: #HavingExpression
// secondaryAggregations not added — not yet implemented.
functions?: [...#Function]
legend?: string
stepInterval?: number
reduceTo?: #ReduceTo
pageSize?: int & >=1
source?: string
})
#LimitBy: close({
keys: [...string]
value: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#ReduceTo: "sum" | "count" | "avg" | "min" | "max" | "last" | "median"
#Limit: int & >=0 & <=10000
#Offset: int & >=0
#MetricAggregation: close({
metricName: string & !=""
timeAggregation: "latest" | "sum" | "avg" | "min" | "max" | "count" | "rate" | "increase"
spaceAggregation: "sum" | "avg" | "min" | "max" | "count" | "p50" | "p75" | "p90" | "p95" | "p99"
reduceTo?: #ReduceTo
temporality?: "delta" | "cumulative" | "unspecified"
})
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -0,0 +1,24 @@
{
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "Hit/s across all hosts",
"stepInterval": 60
}
}

View File

@@ -0,0 +1,12 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/clickhouse_query.go — ClickHouseQuery
kind: "SigNozClickHouseSQL"
spec: close({
name: #QueryName
query: string & !=""
disabled?: bool | *false
legend?: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"

View File

@@ -0,0 +1,9 @@
{
"kind": "SigNozClickHouseSQL",
"spec": {
"name": "A",
"query": "SELECT toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS ts, count() AS total FROM signoz_logs.distributed_logs GROUP BY ts ORDER BY ts",
"disabled": false,
"legend": "Log count"
}
}

View File

@@ -0,0 +1,24 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/req.go — CompositeQuery
// SigNozCompositeQuery groups multiple query plugins into a single
// query request. Each entry is a typed envelope whose spec is
// validated by the corresponding plugin schema.
kind: "SigNozCompositeQuery"
spec: close({
queries: [...#QueryEnvelope]
})
// QueryEnvelope wraps a single query plugin with a type discriminator.
#QueryEnvelope: close({
type: #QueryType
spec: {...}
})
#QueryType:
"builder_query" |
"builder_formula" |
"builder_join" |
"builder_trace_operator" |
"promql" |
"clickhouse_sql"

View File

@@ -0,0 +1,53 @@
{
"kind": "SigNozCompositeQuery",
"spec": {
"queries": [
{
"type": "builder_query",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
}
}
},
{
"type": "builder_query",
"spec": {
"name": "B",
"signal": "metrics",
"expression": "B",
"aggregations": [
{
"metricName": "redis_keyspace_misses",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
}
}
},
{
"type": "builder_formula",
"spec": {
"name": "F1",
"expression": "A / (A + B) * 100",
"legend": "Hit rate %"
}
}
]
}
}

View File

@@ -0,0 +1,10 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozCustomVariable"
spec: close({
customValue: =~"^[^,]+(,[^,]+)*$"
sort?: #VariableSortOrder
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -0,0 +1,7 @@
{
"kind": "SigNozCustomVariable",
"spec": {
"customValue": "production,staging,development",
"sort": "DISABLED"
}
}

View File

@@ -0,0 +1,9 @@
package model
kind: "SigNozDatasource"
// SigNoz has a single built-in backend — the frontend already knows
// the API endpoint, so there is no connection config to validate.
// Add fields here if SigNoz ever supports multiple backends or
// configurable API versions.
spec: close({})

View File

@@ -0,0 +1,4 @@
{
"kind": "SigNozDatasource",
"spec": {}
}

View File

@@ -0,0 +1,11 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozDynamicVariable"
spec: close({
dynamicVariablesAttribute: string
dynamicVariablesSource: string
sort?: #VariableSortOrder
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -0,0 +1,8 @@
{
"kind": "SigNozDynamicVariable",
"spec": {
"dynamicVariablesAttribute": "host_name",
"dynamicVariablesSource": "metrics",
"sort": "ASC"
}
}

View File

@@ -0,0 +1,27 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/formula.go — QueryBuilderFormula
kind: "SigNozFormula"
spec: close({
name: #QueryName
expression: string
disabled?: bool | *false
legend?: string
limit?: #Limit
having?: #HavingExpression
stepInterval?: number
order?: [...#OrderByItem]
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#Limit: int & >=0 & <=10000
#HavingExpression: close({
expression: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})

View File

@@ -0,0 +1,8 @@
{
"kind": "SigNozFormula",
"spec": {
"name": "F1",
"expression": "A / B * 100",
"legend": "Hit rate %"
}
}

View File

@@ -0,0 +1,75 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/join.go — QueryBuilderJoin
kind: "SigNozJoin"
spec: close({
name: #QueryName
left: #QueryRef
right: #QueryRef
type: #JoinType
on: string
disabled?: bool | *false
aggregations?: [...#MetricAggregation]
expressionAggregations?: [...#ExpressionAggregation]
selectFields?: [...]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
having?: #HavingExpression
// secondaryAggregations not added — not yet implemented.
order?: [...#OrderByItem]
limit?: #Limit
functions?: [...#Function]
})
#QueryRef: close({
name: #QueryName
})
#JoinType: "inner" | "left" | "right" | "full" | "cross"
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#ReduceTo: "sum" | "count" | "avg" | "min" | "max" | "last" | "median"
#Limit: int & >=0 & <=10000
#MetricAggregation: close({
metricName: string & !=""
timeAggregation: "latest" | "sum" | "avg" | "min" | "max" | "count" | "rate" | "increase"
spaceAggregation: "sum" | "avg" | "min" | "max" | "count" | "p50" | "p75" | "p90" | "p95" | "p99"
reduceTo?: #ReduceTo
temporality?: "delta" | "cumulative" | "unspecified"
})
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -0,0 +1,11 @@
{
"kind": "SigNozJoin",
"spec": {
"name": "J1",
"left": {"name": "A"},
"right": {"name": "B"},
"type": "inner",
"on": "service.name = service.name",
"disabled": false
}
}

View File

@@ -0,0 +1,14 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/prom_query.go — PromQuery
kind: "SigNozPromQL"
spec: close({
name: #QueryName
query: string & !=""
disabled?: bool | *false
step?: number
stats?: bool
legend?: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"

View File

@@ -0,0 +1,9 @@
{
"kind": "SigNozPromQL",
"spec": {
"name": "A",
"query": "rate(http_requests_total{status=\"200\"}[5m])",
"disabled": false,
"legend": "{{method}} {{path}}"
}
}

View File

@@ -0,0 +1,10 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozQueryVariable"
spec: close({
queryValue: string
sort?: #VariableSortOrder
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -0,0 +1,7 @@
{
"kind": "SigNozQueryVariable",
"spec": {
"queryValue": "SELECT DISTINCT host_name FROM signoz_metrics.distributed_time_series_v4_1day WHERE metric_name = 'redis_cpu_time'",
"sort": "ASC"
}
}

View File

@@ -0,0 +1,68 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/trace_operator.go — QueryBuilderTraceOperator
// SigNozTraceOperator composes multiple trace BuilderQueries using
// relational operators (=>, ->, &&, ||, NOT) to query trace relationships.
// Signal is implicitly "traces" — all referenced queries must be trace queries.
kind: "SigNozTraceOperator"
spec: close({
name: #QueryName
// Operator expression composing trace queries, e.g. "A => B && C".
expression: string & !=""
disabled?: bool | *false
// Which query's spans to return (must be a query referenced in expression).
returnSpansFrom?: #QueryName
aggregations?: [...#ExpressionAggregation]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
order?: [...#OrderByItem]
limit?: #Limit
offset?: #Offset
cursor?: string
functions?: [...#Function]
stepInterval?: number
having?: #HavingExpression
legend?: string
selectFields?: [...]
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#Limit: int & >=0 & <=10000
#Offset: int & >=0
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -0,0 +1,19 @@
{
"kind": "SigNozTraceOperator",
"spec": {
"name": "T1",
"expression": "A => B",
"returnSpansFrom": "A",
"aggregations": [
{
"expression": "count()",
"alias": "request_count"
}
],
"filter": {
"expression": "service.name = 'frontend'"
},
"groupBy": [],
"order": []
}
}

2
perses/setup.sh Executable file
View File

@@ -0,0 +1,2 @@
python3 generate_manifests.py --org signoz --name signoz --version 0.0.1
docker run -d -p 8080:8080 --name perses -v $(pwd)/signoz-0.0.1.tar.gz:/etc/perses/plugins-archive/signoz-plugin.tar.gz persesdev/perses:latest-debug

View File

@@ -0,0 +1,4 @@
./setup.sh
sleep 2
./validate.sh
./deletePostValidate.sh

1
perses/validate.sh Executable file
View File

@@ -0,0 +1 @@
percli lint -f ./examples/perses.json --online