A walkthrough of JUJING OBSERVE's SQL-like search: query logs with WHERE clauses, whitelist validation against injection, an AI assistant that turns plain language into conditions, and drill-down into traces via traceId.
What usually puts people off a logging system isn't ingestion — it's querying. Lucene for Elasticsearch, LogQL for Loki, the Lucene-flavored filters in Kibana: every tool forces you to memorize yet another set of operators, and the syntax you learned last month doesn't transfer to the next stack. JUJING OBSERVE made a deliberate choice here: query logs with SQL-like WHERE clauses, so anyone who has ever written SQL is productive in about a minute.
The search box on the Service page takes a WHERE clause. To find error logs from user-service, type exactly what you'd expect:
level="ERROR" and serviceName="user-service"
The available fields cover the dimensions you actually reach for while debugging: level, serviceName, projectName, companyName, content, traceId, threadName, host, nodeIp, plus the trace-side fields code, cost, path, method, and query. Operators are =, !=, >, >=, <, <=, and LIKE, joined with and / or. A few examples worth keeping around:
level="WARN" and serviceName="order-service"
content LIKE "%timeout%"
cost>=3000
traceId="abc123..."
Two things make this feel better than a DSL in practice. First, the conditions are short, so they paste cleanly into a chat message when you're describing to a teammate exactly what you're chasing. Second, because the field names map to familiar concepts — level, service, trace — you rarely need to consult docs to guess the right column; autocomplete and the AI assistant fill in the rest.
The obvious objection to SQL-like search is injection. JUJING OBSERVE handles it with two layers rather than by dumbing down the query surface:
On top of that, conditions are compiled into parameterized queries: values go through placeholders, never string concatenation. That removes the injection class of problems at the root, which is why the search page can be opened up to every tenant directly instead of hiding query capability behind a trained operator. For a team that used to gate Elasticsearch access through a handful of "search ninjas", this is a genuine operational change: anyone can search, and it's safe by construction.
Not everyone wants to write conditions by hand. The AI assistant on the search page turns plain language into WHERE clauses, using a rule engine by default so it works offline and deterministically:
level="ERROR" and serviceName="user-service"content LIKE "%timeout%"code>=500If you configure an external model (AI_API_URL / AI_API_KEY), the system falls back to the LLM when the rules don't match, and the generated condition still passes the same whitelist validation before it runs. The point isn't which model sits behind it — it's that whatever path generates the condition, the guardrails stay identical. That's what matters when a junior engineer types a vague question and the assistant quietly returns a safe, runnable filter.
Search isn't an island. The search page lets you pick a time window, aggregate by service to see a per-minute timeline, and open any single log to see its parsed fields — time, level, thread name, traceId. From a record you can jump straight to Tracing to see that request's latency, status code, path, and request/response body.
The flow — search, aggregate, drill into one record, follow the traceId — is the order that actually feels right when you're debugging a live issue, because each step narrows the problem space without a context switch. If you'd rather skip straight to wiring it up, the next article covers onboarding your logs in five minutes.