Created
April 24, 2025 21:55
-
-
Save hoel-zr-o/f5121d30e985ead6a39c62346b6718ba to your computer and use it in GitHub Desktop.
PromQL → JSON AST tool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module example.com/alert-rule-experiment | |
go 1.23.6 | |
require github.com/prometheus/prometheus v0.301.0 | |
require ( | |
github.com/beorn7/perks v1.0.1 // indirect | |
github.com/cespare/xxhash/v2 v2.3.0 // indirect | |
github.com/dennwc/varint v1.0.0 // indirect | |
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect | |
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | |
github.com/prometheus/client_golang v1.20.5 // indirect | |
github.com/prometheus/client_model v0.6.1 // indirect | |
github.com/prometheus/common v0.61.0 // indirect | |
github.com/prometheus/procfs v0.15.1 // indirect | |
go.uber.org/atomic v1.11.0 // indirect | |
golang.org/x/sys v0.28.0 // indirect | |
golang.org/x/text v0.21.0 // indirect | |
google.golang.org/protobuf v1.36.0 // indirect | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"encoding/json" | |
"fmt" | |
"os" | |
"github.com/prometheus/prometheus/promql/parser" | |
) | |
func exprToSimpleJSON(expr parser.Expr) any { | |
switch expr := expr.(type) { | |
case nil: | |
return nil | |
case *parser.ParenExpr: | |
return map[string]any{ | |
"type": "paren", | |
"expr": exprToSimpleJSON(expr.Expr), | |
} | |
case *parser.SubqueryExpr: | |
return map[string]any{ | |
"type": "subquery", | |
"expr": exprToSimpleJSON(expr.Expr), | |
"range": expr.Range, | |
"original_offset": expr.OriginalOffset, | |
"offset": expr.Offset, | |
"timestamp": expr.Timestamp, | |
"start_or_end": parser.ItemTypeStr[expr.StartOrEnd], | |
"step": expr.Step, | |
} | |
case *parser.BinaryExpr: | |
var vectorMatching any | |
if expr.VectorMatching != nil { | |
vectorMatching = map[string]any{ | |
"cardinality": expr.VectorMatching.Card.String(), | |
"matching_labels": expr.VectorMatching.MatchingLabels, | |
"on": expr.VectorMatching.On, | |
"include": expr.VectorMatching.Include, | |
} | |
} | |
return map[string]any{ | |
"type": "binary", | |
"op": parser.ItemTypeStr[expr.Op], | |
"lhs": exprToSimpleJSON(expr.LHS), | |
"rhs": exprToSimpleJSON(expr.RHS), | |
"vector_matching": vectorMatching, | |
"return_bool": expr.ReturnBool, | |
} | |
case *parser.AggregateExpr: | |
return map[string]any{ | |
"type": "aggregate", | |
"op": parser.ItemTypeStr[expr.Op], | |
"expr": exprToSimpleJSON(expr.Expr), | |
"param": exprToSimpleJSON(expr.Param), | |
"grouping": expr.Grouping, | |
"without": expr.Without, | |
} | |
case *parser.Call: | |
args := make([]any, 0, len(expr.Args)) | |
for _, arg := range expr.Args { | |
args = append(args, exprToSimpleJSON(arg)) | |
} | |
return map[string]any{ | |
"type": "call", | |
"func": map[string]any{ | |
"name": expr.Func.Name, | |
"arg_types": expr.Func.ArgTypes, | |
"variadic": expr.Func.Variadic, | |
"return_type": expr.Func.ReturnType, | |
}, | |
"args": args, | |
} | |
case *parser.MatrixSelector: | |
return map[string]any{ | |
"vector_selector": exprToSimpleJSON(expr.VectorSelector), | |
"range": expr.Range, | |
} | |
case *parser.VectorSelector: | |
labelMatchers := make([]any, 0, len(expr.LabelMatchers)) | |
for _, labelMatcher := range expr.LabelMatchers { | |
labelMatchers = append(labelMatchers, map[string]any{ | |
"type": labelMatcher.Type.String(), | |
"name": labelMatcher.Name, | |
"value": labelMatcher.Value, | |
}) | |
} | |
return map[string]any{ | |
"name": expr.Name, | |
"original_offset": expr.OriginalOffset, | |
"offset": expr.Offset, | |
"timestamp": expr.Timestamp, | |
"skip_histogram_buckets": expr.SkipHistogramBuckets, | |
"start_or_end": parser.ItemTypeStr[expr.StartOrEnd], | |
"label_matchers": labelMatchers, | |
"bypass_empty_matcher_check": expr.BypassEmptyMatcherCheck, | |
} | |
case *parser.StringLiteral: | |
return expr.Val | |
case *parser.NumberLiteral: | |
return expr.Val | |
default: | |
panic(fmt.Errorf("unhandled expr type %T", expr)) | |
} | |
} | |
func main() { | |
lines := bufio.NewScanner(os.Stdin) | |
for lines.Scan() { | |
query := lines.Text() | |
expr, err := parser.ParseExpr(query) | |
if err != nil { | |
panic(err) | |
} | |
j := exprToSimpleJSON(expr) | |
bytes, err := json.Marshal(j) | |
if err != nil { | |
panic(err) | |
} | |
fmt.Println(string(bytes)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment