How to send logs to non-charmed backends¶
The OpenTelemetry Collector Integrator charm enables exporting telemetry from the charmed OpenTelemetry Collector to non-charmed backends. It acts as a configuration add-on that injects arbitrary exporter configurations, backed by Juju secrets.
This removes the need for Prometheus federation or similar other solutions.
This guide walks through sending logs to an external rsyslog server over TLS, demonstrating how the Integrator handles sensitive material like certificates through its secret templating system. The general approach here can be used with other backends.
Important
Available components (exporters, receivers, processors) depend on the OpenTelemetry Collector build compiled by Canonical. Check the manifest-additions.yaml in the opentelemetry-collector-rock repository for your version to confirm what’s included.
Architecture overview¶
graph LR
subgraph Juju model
app --- opentelemetry-collector
opentelemetry-collector-integrator ---|"external-config"| opentelemetry-collector
end
opentelemetry-collector -.-|"syslog/rfc5424"| non-charmed[Non-charmed e.g. rsyslog]
juju-config@{ shape: braces, label: "Admin config<br> for rsyslog" } -.->|juju config\nconfig_yaml| opentelemetry-collector-integrator
juju-action@{ shape: braces, label: "Optional secrets<br> for rsyslog" } -.->|juju run\ncreate-secret| opentelemetry-collector-integrator
The Integrator injects a syslog exporter and a transform processor into the Collector’s logs pipeline. Once the external-config integration is established, the Collector automatically merges the injected configuration and begins forwarding logs.
Prerequisites¶
A Juju controller and model on a machine cloud (for example, LXD)
The
ubuntucharm deployed in the modelThe
opentelemetry-collectorcharm deployed as a subordinate ofubuntu. This guide names the charmotelcolfor short.An external rsyslog server reachable from the Juju machines, configured to accept TLS connections on port
6514TLS certificate material for mutual TLS: CA certificate, client certificate, and client key
jujuCLI version 3.6 or later
Note
This guide uses the machine (VM) variant of the charms. For Kubernetes, replace opentelemetry-collector with opentelemetry-collector-k8s.
Step 1: Deploy the OpenTelemetry Collector Integrator¶
Deploy the Integrator charm into the same model where otelcol is running:
juju deploy opentelemetry-collector-integrator otelcol-integrator --channel latest/edge
The Integrator enters blocked status until you provide a valid configuration with at least one pipeline enabled.
Step 2: Create a Juju secret with TLS certificates¶
The Integrator manages secrets through a dedicated action. Certificate files must be base64-encoded because the Juju CLI does not preserve newlines in multi-line values.
Create the secret:
juju run otelcol-integrator/leader create-secret \
name=rsyslog-tls \
cafile="$(cat ca.crt | base64 -w0)" \
certfile="$(cat client.crt | base64 -w0)" \
keyfile="$(cat client.key | base64 -w0)"
The output includes the secret identifier:
keys: cafile,certfile,keyfile
secret-id: secret://a1b2c3d4-e5f6-7890-abcd-ef1234567890/csecr3t1d2k3y4l5
Save this secret-id value. You will use it in the next step to build secret reference URIs.
Step 3: Create the exporter configuration¶
Create a file named syslog-tls-exporter.yaml with the following content. Replace the placeholder values with your rsyslog endpoint and the secret-id from Step 2:
processors:
transform/logs-to-syslog:
log_statements:
- context: log
statements:
- set(time, observed_time)
- set(attributes["priority"], 14)
- set(attributes["hostname"], body)
- replace_pattern(attributes["hostname"], "^\\S+\\s+(\\S+)\\s+[\\s\\S]*$", "$1")
- set(attributes["appname"], body)
- replace_pattern(attributes["appname"], "^\\S+\\s+\\S+\\s+([^:\\[]+).*$", "$1")
- set(attributes["message"], body)
- replace_pattern(attributes["message"], "^\\S+\\s+\\S+\\s+\\S+(?:\\[\\d+\\])?:\\s*", "")
exporters:
syslog/rsyslog:
endpoint: your-rsyslog-server.example.com
port: 6514
network: tcp
protocol: rfc5424
tls:
ca_file: "secret://a1b2c3d4-e5f6-7890-abcd-ef1234567890/csecr3t1d2k3y4l5/cafile?render=file"
cert_file: "secret://a1b2c3d4-e5f6-7890-abcd-ef1234567890/csecr3t1d2k3y4l5/certfile?render=file"
key_file: "secret://a1b2c3d4-e5f6-7890-abcd-ef1234567890/csecr3t1d2k3y4l5/keyfile?render=file"
Configuration explained
Transform processor (transform/logs-to-syslog): The syslog exporter reads message content and metadata from log attributes — not from the log body directly. Since the Collector’s filelog receiver passes raw syslog lines as the body (e.g., 2026-04-30T15:38:18+00:00 myhost myapp[123]: Hello world), this processor parses each line to populate those attributes. The statements execute in order:
set(time, observed_time)— overwrites the log timestamp with the time the Collector received the log. The filelog receiver setstimeto zero (epoch) because it does not parse the syslog timestamp from the body.set(attributes["priority"], 14)— sets a static syslog priority (facility 1 × 8 + severity 6 = user-level informational).set(attributes["hostname"], body)+replace_pattern(…, "^\\S+\\s+(\\S+)\\s+[\\s\\S]*$", "$1")— copies the body into the attribute, then strips everything except the second whitespace-delimited field (the hostname).set(attributes["appname"], body)+replace_pattern(…, "^\\S+\\s+\\S+\\s+([^:\\[]+).*$", "$1")— same technique: keeps only the third field up to:or[(the syslog tag, e.g.,test-otelcol).set(attributes["message"], body)+replace_pattern(…, "^\\S+\\s+\\S+\\s+\\S+(?:\\[\\d+\\])?:\\s*", "")— strips the timestamp, hostname, and tag prefix, leaving only the message payload.
With a fixed priority of 14, all logs appear as informational in rsyslog regardless of their actual severity. To preserve severity, replace the static value with conditional OTTL statements that map OpenTelemetry’s severity_number (1–24) to syslog priority values. Syslog priority is calculated as facility × 8 + severity — see RFC 5424 §6.2.1 for the full table.
This processor is applied to all logs in the pipeline, not only those routed to the syslog exporter. The additional attributes do not cause errors in other exporters (such as Loki or OTLP).
Syslog exporter (syslog/rsyslog):
endpoint: hostname or IP of the rsyslog server (no protocol prefix)port: TCP port (6514 is the IANA standard for syslog over TLS)network: must betcpfor TLSprotocol:rfc5424(modern syslog standard)tls: references to the Juju secret using the?render=filedirective — the Collector writes each certificate to disk with restricted permissions and substitutes the file path
The secret URI format is: <secret-id>/<key>?render=<type>, where <secret-id> is the full value returned by the create-secret action, <key> is one of the keys you stored in the secret, and <type> is file (for certificates written to disk) or inline (for values substituted directly in the config text).
Step 4: Apply the configuration¶
Configure the Integrator with the exporter YAML and enable the logs pipeline:
juju config otelcol-integrator \
config_yaml=@syslog-tls-exporter.yaml \
logs_pipeline=true
Verify the Integrator reaches active status:
juju status otelcol-integrator
You should see:
App Version Status Scale Charm Channel Rev Exposed Message
otelcol-integrator active 1 opentelemetry-collector-integrator latest/edge 3 no Pipelines: logs configured
If the charm shows blocked, check juju debug-log --include otelcol-integrator --replay for validation errors in your YAML or pipeline configuration.
Step 5: Integrate the Collector with the Integrator¶
Establish the external-config integration:
juju integrate otelcol:external-config otelcol-integrator:external-config
Once the integration is established, the Integrator shares the configuration and grants access to the Juju secret. The Collector automatically:
Retrieves the secret content
Writes certificate files to disk (for
?render=filereferences)Merges the exporter and processor into its logs pipeline
Reloads its configuration
Note
The otelcol application may remain in blocked status because the external-config integration does not satisfy the charm’s primary outbound integration requirements (such as send-loki-logs or send-otlp). This does not prevent the external exporter from functioning — logs are still forwarded to rsyslog.
Verify the integration is established:
juju status --relations
You should see an external-config integration between otelcol and otelcol-integrator.
Warning
If you are testing with rsyslog running on the same machine as the Collector (for example, using 127.0.0.1 as the endpoint), you must exclude the rsyslog output file from the Collector’s filelog receiver to avoid an infinite log loop. Set the path_exclude configuration option on the otelcol charm to include the rsyslog log path (for example, /var/log/otelcol-remote.log*).
Step 6: Verify logs are reaching rsyslog¶
On your external rsyslog server, start watching the log output:
sudo tail -f /var/log/otelcol-remote.log
Generate a test log using the system logger:
juju ssh ubuntu/0 "logger -t test-otelcol 'Hello from OtelCol Integrator'"
Within a few seconds, you should see the message appear in your rsyslog output file:
2026-04-30T16:27:32.071582Z juju-ec9344-0 test-otelcol Hello from OtelCol Integrator via syslog :)
Cross-model relations¶
If your Integrator and Collector are deployed in different Juju models, use cross-model relations (CMR):
# From the Integrator's model, offer the endpoint:
juju offer otelcol-integrator:external-config
# From the Collector's model, consume and integrate:
juju consume <controller>:admin/<integrator-model>.otelcol-integrator
juju integrate otelcol:external-config otelcol-integrator:external-config
For more details, see the Juju cross-model relations documentation.