JuJing OBSERVE brings SQL-style queries to log search — field filtering, piped aggregation, and inverted-index acceleration replace slow grep scans. This article breaks down the query syntax, indexing, and four real-world queries.
The most common move when debugging production is grep for a keyword, then tail a few hundred lines of context. That works on small logs, but three problems grow quickly with log volume.
First, grep is a sequential scan with no index. Scanning a 50 GB single-day log file takes minutes; run a few of those scans and debugging becomes waiting. Second, grep only does text matching — it can't do structured filtering. "Find 5xx requests whose duration exceeded 2 seconds" is not expressible in grep. Third, grep results can't be aggregated. If you want per-service error rates or an error-over-time histogram, you have to write a separate script.
JuJing OBSERVE treats logs like a table you can query: define fields first, then let the search engine understand filtering, sorting, and aggregation.
A query has three stages — filter, process, aggregate:
status >= 500 AND service = "order"
| parse duration_ms
| stats count() by service, status
status >= 500, service = "order", message like "%timeout%", with AND/OR/NOT and parentheses.parse extracts fields from text, extend derives new fields, where re-filters.stats, bucket, top return results directly — no export to a spreadsheet.Fields get automatic type inference. duration_ms=183 in a log line becomes a number, not a string; status=500 an integer; timestamp a time. That's what makes status >= 500 a numeric comparison instead of a string one.
The engine does two things at write time: partition by time and build inverted indexes on field values.
service = "order" into a direct lookup of matching document IDs, dropping complexity from O(n) to roughly O(matches).On a single node with 8 cores and 32 GB RAM, a two-condition filter over one billion log lines holds P99 latency under 800 ms.
status >= 500 | stats count() as err by service
parse "duration_ms=%{number:ms}" | where ms > 2000 | top ms limit 10
status >= 500 | bucket span=5m | stats count() by _time
Save common queries as templates and attach them to alerts or dashboards. When something breaks, you click once instead of hand-writing syntax under pressure.