On-premise Gurubase deployments include Fluent Bit for log collection. By default, logs from all Gurubase services are written to a local file. You can add an OpenSearch output to forward these logs to a centralized OpenSearch cluster for real-time search, dashboards, and alerting.This guide walks you through setting up a dedicated OpenSearch user with minimal permissions, configuring Fluent Bit to ship logs securely, and setting up Slack alerts for error detection.
Fluent Bit tails Docker container logs from all Gurubase services, enriches them with metadata (container name, version), parses JSON-formatted logs from Python services into structured fields, and forwards them to both a local file and your OpenSearch cluster.
It’s recommended to create a dedicated user with write-only permissions
instead of using your OpenSearch admin credentials in Fluent Bit configuration
files.
Run the following three commands against your OpenSearch cluster.
Replace the following placeholder values before running the commands:
Placeholder
Replace with
YOUR_ADMIN_PASSWORD
Your OpenSearch admin password
YOUR_OPENSEARCH_HOST
Your OpenSearch cluster hostname
YOUR_OPENSEARCH_PORT
Your OpenSearch port (443 for hosted, 9200 for self-managed)
Add the following [OUTPUT] block to your Fluent Bit configuration file. The existing file output is preserved. Fluent Bit supports multiple outputs and will send each log record to all matching outputs.
The Fluent Bit configuration file is located at
~/.gurubase/config/fluent-bit.conf on the host machine.
Replace YOUR_OPENSEARCH_HOST and CHOOSE_A_STRONG_PASSWORD below with the
actual values from Step 1.
[OUTPUT] Name opensearch Match docker.* Host YOUR_OPENSEARCH_HOST Port YOUR_OPENSEARCH_PORT HTTP_User gurubase_fluentbit_writer HTTP_Passwd CHOOSE_A_STRONG_PASSWORD Logstash_Format On Logstash_Prefix gurubase-onprem-logs Logstash_DateFormat %Y.%m.%d Suppress_Type_Name On tls On tls.verify On Trace_Error On Replace_Dots On Buffer_Size False
Set Port to your OpenSearch cluster’s port. Common values: 443 for
managed/hosted clusters behind a load balancer, 9200 for self-managed
clusters. Set Host to just the hostname (e.g., opensearch.example.com),
without https://.
Once logs are flowing, set up OpenSearch Dashboards for browsing and searching.
OpenSearch Dashboards is typically available at
http://YOUR_OPENSEARCH_HOST:5601. Open it in your browser and log in with
your OpenSearch admin credentials.
1
Open OpenSearch Dashboards
Navigate to http://YOUR_OPENSEARCH_HOST:5601 in your browser and log in with your admin credentials.
2
Create an Index Pattern
Go to Management > Dashboards Management > Index patterns and click Create index pattern. Enter gurubase-onprem-* as the pattern and Click Next step and select @timestamp as the time field and click Create index pattern.
3
Browse Logs in Discover
Go to Discover, select your new index pattern, and adjust the time range. Python service logs (backend, Celery) have structured fields like level, message, traceback, logger, module, and function. Non-Python services have a plain log field.
4
Filter and Search
Use the search bar with KQL syntax to filter logs:
level: ERROR (all error-level logs from Python services)
traceback: * (all logs with a stack trace)
container_name: *postgres* (PostgreSQL logs only)
log: *error* (plain-text logs containing “error”)
container_name: *celery* and level: ERROR (Celery errors)
5
Save Searches (Optional)
Save useful filter combinations as Saved Searches, then add them to a Dashboard for an overview panel.
OpenSearch’s Index State Management (ISM) can automatically delete old log indices to control disk usage. The following policy deletes indices older than 14 days.
Replace YOUR_OPENSEARCH_HOST and YOUR_ADMIN_PASSWORD with your actual
values. Adjust min_index_age to your desired retention period (e.g., 7d,
30d, 60d).
OpenSearch’s Alerting plugin can automatically detect error logs and send notifications to Slack. This section sets up a monitor that checks for error-level logs every 5 minutes and sends a Slack message when errors are detected.
Open https://api.slack.com/apps and click
Create New App → From scratch. Name it Gurubase Onprem Alerts.
2
Enable Incoming Webhooks
In the app settings, navigate to Features → Incoming Webhooks and
toggle it On.
3
Add a new webhook
Click Add New Webhook to Workspace, select the channel you want alerts
sent to (e.g., #gurubase-alerts), and click Allow.
4
Copy the webhook URL
Copy the generated webhook URL. It looks like
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX.
You will need this in the next step.
This monitor runs every 5 minutes. It queries all gurubase-onprem-* indices for error-level structured logs from Python services (using the level and traceback fields) as well as plain-text error patterns from non-Python services (using the log field) within the last 5-minute window. If any matches are found, it sends a summary to Slack.
Replace the following placeholder values before running the command:
Throttling is enabled at 15 minutes. Even if the monitor fires every 5
minutes, Slack will receive at most one notification per 15 minutes. Adjust
the throttle.value to control notification frequency.
Replace MONITOR_ID with the _id returned when you created the monitor. The
dry run response shows whether the trigger condition would fire based on
current data.
To verify the full notification flow, you can wait for an actual error to occur or temporarily lower the trigger threshold. Once verified, you should see a message in your Slack channel similar to:
Replace MONITOR_ID with the _id returned when you created the monitor.
This command fetches the current monitor definition, sets enabled to
false, and updates it in place, preserving all existing configuration
including Slack actions. These commands require
jq to be installed.
The default query matches structured level fields (ERROR, FATAL), the traceback field, and plain-text patterns in the log field. To also match warnings from Python services, add a term clause for WARNING:
{"term": {"level.keyword": "WARNING"}}
For non-Python service logs, modify the query_string in the monitor’s input query. For example, to also match warnings:
log:(*error* OR *exception* OR *traceback* OR *fatal* OR *warn*)
Plain-text patterns must be lowercase because OpenSearch’s default analyzer lowercases indexed text.
Filter by specific service
To alert only on errors from a specific Gurubase service, add a term filter to the bool.filter array:
You can also create monitors visually through OpenSearch Dashboards → Alerting → Monitors → Create monitor. The Dashboards UI provides a form-based editor that is equivalent to the REST API approach documented above.
Gurubase uses JSON structured logging for Python services (backend, Celery workers, Celery beat). Each log entry, including full stack traces, is emitted as a single JSON line. Fluent Bit automatically parses these JSON logs into structured OpenSearch fields. Non-Python services (PostgreSQL, Redis, Milvus) continue to emit plain-text logs.
You can pretty-print JSON logs in the terminal with jq:
If Fluent Bit logs show no permissions for [indices:data/write/bulk[s]], the role may be missing wildcard write permissions. Update the role to use indices:data/write/* instead of individual write actions:
If your OpenSearch cluster uses a self-signed certificate, set tls.verify Off in the Fluent Bit output configuration. For production, it is recommended
to use a valid certificate and keep verification enabled.
Connection refused or timeout
Ensure the Fluent Bit container can reach your OpenSearch cluster. Check that
the hostname resolves correctly and the port is accessible. If OpenSearch is
behind a firewall or VPN, the Gurubase host must have network access.
No data appearing in OpenSearch
Check Fluent Bit logs: docker logs gurubase-fluent-bit --tail 100 2.
Verify the container name grep filter matches your deployment prefix 3. Ensure
the Fluent Bit flush interval (default 5 seconds) has elapsed 4. Confirm the
index pattern in OpenSearch Dashboards matches the index name
Slack alerts not being sent
Verify the notification channel exists: curl -u 'admin:YOUR_ADMIN_PASSWORD' 'https://YOUR_OPENSEARCH_HOST/_plugins/_notifications/configs/NOTIFICATION_CHANNEL_ID'
Send a test notification: curl -u 'admin:YOUR_ADMIN_PASSWORD' 'https://YOUR_OPENSEARCH_HOST/_plugins/_notifications/feature/test/NOTIFICATION_CHANNEL_ID'
Check if throttling is suppressing notifications. The default throttle is
15 minutes 4. Verify the Slack webhook URL is still valid in your Slack app
settings 5. Check monitor execution status: curl -u 'admin:YOUR_ADMIN_PASSWORD' 'https://YOUR_OPENSEARCH_HOST/_plugins/_alerting/monitors/alerts'
Monitor error: no mapping found for message.keyword
If the monitor alert shows IllegalArgumentException[no mapping found for message.keyword in order to collapse on], the monitor query is using a collapse clause on the message.keyword field. Non-Python services (PostgreSQL, Redis, Milvus) don’t have a message field — they use log instead. When an index contains only non-Python service logs, the message field mapping doesn’t exist, causing the collapse (and the entire query) to fail.Fix: Remove the collapse clause from the monitor’s input query. The size: 10 limit already restricts the number of results returned. Update the monitor via the REST API or through OpenSearch Dashboards → Alerting → Monitors → edit the monitor → remove the collapse from the extraction query.
Change the plugin name from opensearch to es. All other parameters remain the same:
[OUTPUT] Name es Match docker.* Host YOUR_ELASTICSEARCH_HOST Port YOUR_ELASTICSEARCH_PORT HTTP_User gurubase_fluentbit_writer HTTP_Passwd CHOOSE_A_STRONG_PASSWORD Logstash_Format On Logstash_Prefix gurubase-onprem-logs Logstash_DateFormat %Y.%m.%d Suppress_Type_Name On tls On tls.verify On Trace_Error On Replace_Dots On Buffer_Size False
Suppress_Type_Name On is required for Elasticsearch 8.x+ (just like
OpenSearch 2.x+). For Elasticsearch 7.x, you can omit it.