Logging and monitoring
Enterprise Studio writes structured, machine-parseable logs to standard output.
Log output destination
All logs are written to stdout.
-
Binary: redirect stdout to a file and use an external tool such as
logrotate. Enterprise Studio does not rotate logs internally. -
Docker: logs are available through
docker logsand forwarded to the configured Docker logging driver. -
Kubernetes: container stdout is collected by the kubelet and available through
kubectl logs. Use a log aggregator such as Fluentd, Fluent Bit, or Promtail to ship logs to a centralized system (for example Loki, Elasticsearch, or Splunk).
Retrieving logs
Because Enterprise Studio only writes to stdout, collecting logs means capturing the output of the process or container. The following examples follow the destinations above.
Binary
Redirect stdout and stderr to a file when you start Enterprise Studio.
On Linux:
./nes >> logs.txt 2>&1
On Windows:
.\nes.exe >> logs.txt 2>&1
|
Enterprise Studio does not rotate logs internally.
Use an external tool such as |
Docker
Read the logs of a running container:
docker logs <CONTAINER-NAME>
Save them to a file.
docker logs writes the container’s stdout to your stdout and its stderr to your stderr, so redirect both:
docker logs <CONTAINER-NAME> > logs.txt 2>&1
If you did not name the container with --name, find it with docker ps.
|
For production, configure a Docker logging driver so container stdout is shipped to your logging system. |
Kubernetes
The examples below use the release name my-enterprise-studio from Deploy to Kubernetes.
Read the logs of all pods in the release:
kubectl logs -l app.kubernetes.io/instance=my-enterprise-studio
Follow the logs:
kubectl logs -f -l app.kubernetes.io/instance=my-enterprise-studio
Save the full log history to a file.
By default kubectl logs truncates the output when selecting by label, so pass --tail=-1:
kubectl logs -l app.kubernetes.io/instance=my-enterprise-studio --tail=-1 > logs.txt
If the pod restarted, read the logs of the previous container to find out why it exited:
kubectl logs <POD-NAME> --previous
|
For production, use a cluster-level logging agent such as Fluentd, Fluent Bit, or Promtail to ship container stdout to a centralized system. Kubernetes does not store logs independently of the node or pod; see the Kubernetes logging architecture documentation. |
Reading and filtering logs
In the default json format, each line is a self-contained JSON object, so you can filter logs with a tool such as jq.
Show only errors:
jq 'select(.level == "ERROR")' logs.txt
Condense a live log stream to one readable line per entry:
docker logs -f <CONTAINER-NAME> 2>&1 | jq -r '"\(.time) \(.level) \(.msg)"'
Follow all entries belonging to a single request using its request_id:
jq 'select(.request_id == "a1b2c3d4-e5f6-7890-abcd-ef1234567890")' logs.txt
Log format
Every JSON log entry contains the following top-level fields:
| Field | Description |
|---|---|
|
ISO 8601 timestamp of the log event. |
|
Log level: |
|
Human-readable log message. |
|
The internal subsystem that produced the entry (for example |
|
UUID generated per incoming HTTP request, useful for correlating log entries. |
|
Object containing |
|
Error details (present only when an error occurred). |
Example JSON log entry:
{
"time": "2025-06-10T14:32:01.456Z",
"level": "INFO",
"msg": "POST /api/shared-storage/v1/cypher 200 34ms",
"component": "storageApi",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user": {
"username": "[email protected]",
"roles": ["editor"],
"deployment": "production"
}
}
Two output formats are available, controlled by the logs.format setting: json (default) produces structured JSON with one object per line; pretty produces colorized human-readable lines for local development.
See Configuration → Logging for details.
|
At |
Access logs
When assetStore.default.logs.requestLogs.enabled is true (the default), Enterprise Studio writes a structured log entry for every Storage API HTTP request.
Example access log entry:
{
"time": "2025-06-10T14:35:12.789Z",
"level": "INFO",
"msg": "GET /api/shared-storage/v1/artifacts 200 12ms",
"component": "storageApi",
"request_id": "f7e6d5c4-b3a2-1098-fedc-ba9876543210",
"protocol": "HTTP/1.1",
"remote_ip": "10.0.0.5",
"method": "GET",
"uri": "/api/shared-storage/v1/artifacts",
"status_code": 200,
"latency": "12ms",
"response_size": 4096,
"user_agent": "Mozilla/5.0"
}
Compared to a standard log entry, access log entries add the protocol, remote_ip, method, uri, status_code, latency, response_size, and user_agent fields.
Since every request-scoped log entry includes the authenticated username, roles, and deployment, access logs provide an audit trail out of the box.
Health check endpoint
Enterprise Studio exposes a health check endpoint for use with load balancers and container orchestration platforms:
| Method | Path | Response |
|---|---|---|
|
|
|
This is a simple liveness check that confirms the HTTP server is running. It does not verify backend connectivity (for example to the storage database).
Storage API query profiling
The Storage API performs Cypher queries on the storage database. Enabling query profiling adds execution statistics to query logs, which can help diagnose slow operations.
To enable query profiling, set assetStore.default.queryProfiling.enabled to true.
This setting is disabled by default.
When enabled, Storage API data queries use the Cypher PROFILE keyword and the resulting metrics are added to log entries.
|
Query profiling logs are written at |
The following metrics are added to the profile_debug_stats object:
| Metric | Description |
|---|---|
|
Total database hits. See Cypher Manual → Database hits for more information. |
|
Number of records returned by the query. |
|
Database rows scanned for leaf operators in the query profile. |
|
Indexes used by the query. |
Example log entry with profiling enabled:
{
"time": "2025-06-10T14:32:01.789Z",
"level": "DEBUG",
"msg": "Execute Cypher Query",
"component": "storageApi",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"profile_debug_stats": {
"documents_scanned": "2",
"records_returned": "1",
"total_database_db_hits": "22",
"used_indexes": [
{"name": "used_index_1"},
{"name": "used_index_2"}
]
}
}
Generative AI requests
When generative AI is enabled, every proxied call to the LLM provider writes two info entries.
Generative AI request received is written before the request body is read, so a call rejected as too large or malformed still appears.
Generative AI request completed is written after the provider answers.
Example log entry:
{
"time": "2025-06-10T14:41:22.104Z",
"level": "INFO",
"msg": "Generative AI request completed",
"request_id": "c3d4e5f6-a7b8-9012-cdef-3456789abcde",
"referringClient": "upx:dashboards",
"durationMs": 2841,
"promptTokens": 1204,
"completionTokens": 733,
"totalTokens": 1937
}
The request_id links the two entries for a single call.
A completion that the provider cut short still returns 200 OK with truncated content, and is logged as a warning:
{
"time": "2025-06-10T14:41:22.100Z",
"level": "WARN",
"msg": "LLM provider did not finish the completion",
"finishReason": "length",
"maxCompletionTokens": 16384,
"completionTokens": 16384
}
A finishReason of length means settings.maxCompletionTokens is too low for that feature.
|
At |
Redaction
Passwords, SSO client secrets, and the generative AI provider API key are replaced with [REDACTED] in log output.
The values of generativeAi.chatCompletionProvider.default.upstream.headers are redacted as well; the header names remain visible so you can confirm which headers are sent.
Usernames, IP addresses, user agent strings, and Cypher query parameters (at debug level) are not redacted.
To remove these from stored logs, mask them in your log pipeline before long-term storage.
Error responses
The Storage API returns error responses using the RFC 9457 problem details format.
Each response contains a type (machine-readable identifier), title (human-readable summary), and optionally detail and errors (for batch failures).
{
"type": "not-found",
"title": "resource 'dashboard/abc-123' not found"
}
The type field distinguishes errors that share the same HTTP status code — for example not-found vs not-found-parent (both 404), or validation-error (422) vs json-syntax-error (400).
These values are stable and can be used in alerting rules.
The same type value appears in the corresponding structured log entry.