Some applications produce log entries that span multiple lines. Common examples include Java stack traces, Python tracebacks, and multi-line JSON objects. By default, the agent treats each line as a separate log entry, which breaks these into fragmented, hard-to-read pieces in the Logs UI.
Multiline configuration lets you define rules that recombine related lines into a single, coherent log entry before it is sent to the OpsRamp Platform.
Prerequisites
- Log Management is enabled for your client.
- Pod log collection is enabled. For details, see Configuring Pod Log Collection.
- OpsRamp Kubernetes 2.0 Agent version 22.0.0 or later.
How it works
You define multiline rules as an array under multiline in the opsramp-logs-user-config ConfigMap. Each rule specifies:
- A pattern that identifies where a log entry starts or ends.
- Optional filters that scope the rule to a specific namespace, pod, or container.
You can define multiple rules in the same array, each targeting a different application or log format.
kubectl edit configmap opsramp-logs-user-config -n <agent-installed-namespace>
pods:
enable: true
multiline:
- line_start_pattern: '<regex>'
namespaces:
- <namespace>
pods:
- <workload-name>
containers:
- <container-name>
Field reference
| Field | Type | Required | Description |
|---|---|---|---|
line_start_pattern | String | One of line_start_pattern or line_end_pattern is required. | Regular expression that matches the first line of a multi-line log entry. |
line_end_pattern | String | One of line_start_pattern or line_end_pattern is required. | Regular expression that matches the last line of a multi-line log entry. |
namespaces | String array | No | Specifies the namespaces to which this rule applies. If omitted, the rule applies to all namespaces. |
pods | String array | No | Specifies the workload names (Deployment, StatefulSet, or DaemonSet) to which this rule applies. If omitted, the rule applies to all pods. |
containers | String array | No | Specifies the container names to which this rule applies. If omitted, the rule applies to all containers. |
Important
Each rule must specify exactly one ofline_start_pattern or line_end_pattern; never both. A rule with neither is ignored.Scoping rules with filters
Filters narrow which pods a rule applies to. All three dimensions—namespace, pod, and container—must match (they work as AND conditions), while multiple values within a single filter are OR-ed.
1. Namespaces
namespaces:
- production
- staging
The rule applies if the log comes from either namespace listed.
2. Pods
Provide the workload name — the Deployment, StatefulSet, DaemonSet, or Job name — and not the full pod name. You do not need to include the replica hash suffix.
pods:
- payment-service
This automatically matches pods like payment-service-6b8f9c7d5-abc12.
| Workload Type | What to Write | Example Pod It Matches |
|---|---|---|
| Deployment | payment-service | payment-service-6b8f9c7d5-abc12 |
| StatefulSet | mysql | mysql-0, mysql-1 |
| DaemonSet | fluentbit | fluentbit-x9z2k |
| Full pod name | mysql-0 | mysql-0 (exact StatefulSet pod) |
Tip
For StatefulSet pods with stable names (e.g.,mysql-0), you can reference the full pod name directly. Avoid doing this for Deployment pods, their full names include an ephemeral hash suffix that changes on every restart, which will break your rule.3. Containers
Provide the exact container name as defined in spec.containers[].name in the pod manifest.
containers:
- app
- sidecar
4. Omitting filters
| Omitted Field | Effect |
|---|---|
No namespaces | Matches logs from all namespaces. |
No pods | Matches logs from all pods. |
No containers | Matches logs from all containers. |
| No filters specified | Acts as a global rule and applies to every pod log in the cluster. |
Warning: Risk of global rules
A rule with no namespaces, pods, or containers filters applies to every pod in the cluster which can cause serious problems if different applications use different log formats.
Example: a global rule with a timestamp pattern:
pods:
enable: true
multiline:
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
This works correctly for Java apps that start every entry with a date like 2024-06-10. But consider what happens to an application whose logs do not match that pattern, like Nginx access logs: every line gets merged into a single, ever-growing entry, because none of them ever matches the start pattern.
How to avoid this
Prefer scoped rules over global ones:
pods:
enable: true
multiline:
# Scoped to Java apps using this timestamp format
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
pods:
- payment-service
- order-service
# Scoped to Python apps with bracket timestamps
- line_start_pattern: '^\['
pods:
- ml-worker
- cache-service
With this approach, pods not listed in any rule (like Nginx) are unaffected; their logs pass through as normal single-line entries.
If you must use a global rule, place more specific rules before it, and make sure the global pattern matches the majority of applications in your cluster:
pods:
enable: true
multiline:
# Specific rule for Python apps — evaluated first
- line_start_pattern: '^\['
pods:
- ml-worker
# Global fallback — only safe if most apps use this format
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
Even with this ordering, any pod whose logs do not match the global pattern will still have all its lines merged. Use global rules with caution.
Pattern reference
line_start_pattern — a new entry starts whenever a line matches. All subsequent non-matching lines are appended to it.
| Log Format | Pattern | Example First Line |
|---|---|---|
| ISO timestamp | ^\d{4}-\d{2}-\d{2} | 2024-01-15 10:23:45 ERROR ... |
| Log level prefix | ^(ERROR\|WARN\|INFO\|DEBUG) | ERROR Something failed |
| Python traceback | ^Traceback | Traceback (most recent call last): |
| Bracket timestamp | ^\[ | [2024-01-15T10:23:45Z] INFO ... |
line_end_pattern — an entry ends whenever a line matches. The next line starts a new entry.
| Log Format | Pattern | Example Last Line |
|---|---|---|
| JSON closing brace | ^} | } (matches only when } appears at the beginning of the line; indented closing braces do not match) |
| End marker | ^---$ | --- |
Examples
Example 1: Java stack traces (using line_start_pattern)
pods:
enable: true
multiline:
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
pods:
- payment-service
This rule means: “A new entry starts whenever a line begins with a date like 2024-06-10. Everything else belongs to the previous entry."
| Line | Matches Pattern? | Action |
|---|---|---|
2024-06-10 14:05:22 ERROR ... | Yes | Starts log entry A. |
java.lang.NullPointerException: null | No | Appended to log entry A. |
at com.example.PaymentService... | No | Appended to log entry A. |
at com.example.OrderController... | No | Appended to log entry A. |
2024-06-10 14:05:23 INFO ... | Yes | Completes log entry A and starts log entry B. |
Result in OpsRamp: the first four lines appear as a single log entry; the fifth starts a new one.
Example 2: Multi-line JSON logs (using line_end_pattern)
pods:
enable: true
multiline:
- line_end_pattern: '^}'
pods:
- json-logger
This rule means: “An entry ends whenever a line starts with }. The next line starts a new entry."
| Line | Matches ^}? | Action |
|---|---|---|
{ | No | Starts log entry A. |
"timestamp": "2024-..." | No | Appended to log entry A. |
"level": "error", | No | Appended to log entry A. |
"message": "Connection timeout" | No | Appended to log entry A. |
} | Yes (at the start of the line) | Completes log entry A. |
{ | No | Starts log entry B. |
Result in OpsRamp: the first five lines form one JSON log entry; the sixth starts the next.
Note
If your JSON has nested objects, inner closing braces are typically indented (for example,}). Since ^} requires the brace at column 1, nested braces will not match, and the entry will not split prematurely, as long as your application’s JSON output uses consistent indentation.Example 3: Multiple rules for different applications
pods:
enable: true
multiline:
# Rule 1: Java apps in production
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
namespaces:
- production
pods:
- payment-service
containers:
- app
# Rule 2: Python tracebacks for ML worker
- line_start_pattern: '^Traceback|^\d{4}-\d{2}-\d{2}'
pods:
- ml-worker
# Rule 3: Bracket-prefixed logs in the monitoring namespace
- line_start_pattern: '^\['
namespaces:
- monitoring
| Pod | Namespace | Rule Applied | Why |
|---|---|---|---|
payment-service-xxx (container app) | production | Rule 1 | Most specific match because the namespace, pod, and container all match. |
payment-service-xxx | staging | None | No rule matches, so logs are collected as single-line entries. |
ml-worker-xxx | Any | Rule 2 | Matches based on the pod name. |
prometheus-xxx | monitoring | Rule 3 | Matches based on the namespace. |
nginx-xxx | default | None | No rule matches, so logs are collected as single-line entries. |
Example 4: Scoping a rule to namespace, pod, and container together
pods:
enable: true
multiline:
- line_start_pattern: '^\d{4}-\d{2}-\d{2}'
namespaces:
- production
pods:
- payment-service
containers:
- app
| Pod | Namespace | Container | Applied? | Why |
|---|---|---|---|---|
payment-service-xxx | production | app | Yes | All three filters match. |
payment-service-xxx | production | sidecar | No | The container name does not match. |
payment-service-xxx | staging | app | No | The namespace does not match. |
order-service-xxx | production | app | No | The pod name does not match. |
Common use cases
| Scenario | Configuration |
|---|---|
| Java stack traces for a specific application | line_start_pattern: '^\d{4}-\d{2}-\d{2}' with pods: [order-service] |
| Python tracebacks across the cluster | line_start_pattern: '^Traceback' with no filters (global rule) |
| Multi-line JSON logs in a specific namespace | line_end_pattern: '^}' with namespaces: [logging] |
| Different multi-line patterns for different applications | Define multiple rules, each with its own pattern and namespace, pod, or container filters. |
Important notes
- Each rule must specify exactly one of
line_start_patternorline_end_pattern. Do not specify both, and do not omit both. - The
multilinefield accepts an array, so multiple rules can coexist, each scoped to different workloads or namespaces. - Logs that do not match any multiline rule are sent as single-line entries; no logs are dropped.
- Changes to the ConfigMap are picked up automatically within a few minutes. No manual agent restart is required.
- Multiline configuration for pod logs is separate from multiline configuration for node logs. Node logs continue to use the existing single-object multiline format defined inside each
configsentry. For details, see Node Logs (Syslog).
Troubleshooting
If you encounter kube events issues, see the Troubleshooting documentation.