Skip to main content

Pipeline Processing Architecture

Pipeline Syntax

pipeline JSON has 2 components

Nodes

{
"name": "node-01",
"type": "source",
"node_ref": "file_log",
"config": {
"include_path": ["/var/log/*.log"]
},
"outputs": ["out"],
"multiprocessor": "multiprocessor-01",
"supports": ["metrics", "logs", "traces"],
"active": ["metrics", "logs"],
"credential_ref": "cred_name"
}
NameAcceptable ValuesMandatoryDescription
namenon-empty stringYesuserdefined node name which is configured in the UI
typesource, processor, destination, pack, connectorYesnode category
node_refvalid node definition identifierYesnode reference present in the node definition to classify the node
configJSON object per node schemaYesoutput of JSONForms data for the node
inputsarray of input port namesDepends (mandatory for destinations)list of input ports supported by the node
outputsarray of output port namesDepends (mandatory for sources)list of output ports supported by the node
multiprocessorstring (processor-only)Noname of the multiprocessor if part of any (only applicable when type is processor and part of multiprocessor in UI)
supportsmetrics, logs, tracesYesthe set of signals the node can handle
activemetrics, logs, tracesYesthe type of signals the node is configured for
credential_refnon-empty stringNooptional field only set when the source requires credentials to be set

Connections

{
"to": "batch:in",
"from": "devnull:out"
}

connections define the relationship between 2 nodes, where to specifies the source node and from specifies the target node. The connection is always assumed to be directed to -> from

The format for the values in "to" and "from" is

node_name:port_name

Example

{
"nodes": [
{
"name": "user defined node name",
"type": "source|processor|destination|pack|connector",
"node_ref": "backend node_ref id for the node",
"config": {},
"outputs": ["out"],
"multiprocessor": "name of multiprocessor if part of any",
"supports": ["metrics", "logs", "traces"],
"active": ["metrics", "logs"]
}
],
"connections": [
{
"to": "batch-1756882307193-wactgn:in",
"from": "windows_events_system:out"
}
]
}

Packs Definition

packs are an logical construct which have a mini pipeline defined inside it.

A pack in a complete pipeline is represented in the following way

Note: Packs are always restricted to a single type of Signal either logs, metrics or traces

{
"name": "pack-1",
"type": "pack",
"pack_ref": "pack-1",
"pack_version": "2.0.0",
"inputs": ["in_1", "in_2"],
"outputs": ["out"],
"supports": ["metrics"],
"active": ["metrics"]
}

actual pack definition is as follows

{
"nodes": [
{
"name": "proc-1",
"type": "processor",
"node_ref": "batch",
"config": {},
"outputs": ["out"],
"multiprocessor": "name of multiprocessor if part of any",
"supports": ["metrics", "logs", "traces"],
"active": ["metrics", "logs"]
},
{
"name": "proc-2",
"type": "processor",
"node_ref": "batch",
"config": {},
"outputs": ["out"],
"multiprocessor": "name of multiprocessor if part of any",
"supports": ["metrics", "logs", "traces"],
"active": ["metrics", "logs"]
}
],
"connections": [
{
"to": "batch:in",
"from": "windows_events_system:out"
}
]
}

Predefined Source or Destination nodes

Substituting predefined source or destination node

{
"nodes": [
{
"name": "user defined node name",
"type": "source|destination",
"template_ref": "title_of_predefined_source/dest",
"inputs": ["in"],
"outputs": ["out"]
}
],
"connections": [
{
"to": "batch-1756882307193-wactgn:in",
"from": "windows_events_system:out"
}
]
}

The payload for predefined nodes is same as the normal ones but only difference being they will have a template_ref key with the source or dest name which they want to use and no config

Kubernetes-Aware Conversion

For pipelines that use Kubernetes source nodes (k8s_logs, k8s_kubeletstats, k8s_hostmetrics, k8s_cluster, k8s_events, k8s_appmetrics, k8s_otlp), Praxis converts the pipeline into one or more OpenTelemetryCollector custom resources that the in-cluster Praxis Collector supervisor applies to your cluster.

How the conversion works

  1. Build the pipeline — Praxis produces the same OpenTelemetry configuration it would for any pipeline.
  2. Group by workload — The configuration is split into per-workload groups based on the kind of Kubernetes signal each source collects (for example logs, kubelet metrics, cluster events). Each group becomes its own collector workload.
  3. Merge node-level signals — When a pipeline collects two or more node-level signals (logs, kubelet metrics, host metrics), Praxis collapses them into a single node agent group by default, so the fleet runs one DaemonSet pod per node instead of one per signal. This roughly halves per-node overhead and the number of managed agents. The trade-off is a shared rollout cadence and a wider blast radius if that pod restarts, so it can be turned off when per-signal isolation matters more than pod count.
  4. Filter each group — Every group carries only the receivers, processors, exporters, and extensions its own pipelines actually reference.
  5. Package as a custom resource — Each filtered group is wrapped in an OpenTelemetryCollector custom resource. Its workload mode (DaemonSet, Deployment, or StatefulSet) and the host paths it mounts are derived automatically from the sources in that group — for example a log source brings the host paths needed to tail container logs, and a host-metrics source mounts the host filesystem.

Migration safety

On-node file offsets (the record of how far each log file has been read) are keyed to the pipeline, not to the individual signal workload. This means that when a pipeline switches between per-signal collectors and a merged node agent, the new pod re-uses the existing offsets instead of starting over and re-reading every log file from the beginning. This behavior is covered by automated tests.

Rollout posture

  • Node-agent merging is on by default. When a pipeline changes shape, the supervisor brings the new workload up and confirms it is ready before removing the old one, so signals are not dropped during the transition. Merging can be disabled to fall back to per-signal collectors — useful when you want per-signal blast-radius isolation more than a lower per-node pod count.
  • The supervisor treats each generated workload as an opaque custom resource and does not special-case any signal, so merged and per-signal layouts are handled the same way.

Placement on tainted nodes

When node-level signals are merged, Praxis takes the union of each source's placement settings so the merged node agent still lands where it should:

  • Tolerations — if any merged source opts into tolerating node taints, the merged DaemonSet tolerates them, so log collection on tainted nodes does not silently stop after a merge.
  • Priority class — the first configured priority class is applied.
  • Log-specific settings (such as log path, container runtime type, and multi-line recombination) come from the log source in the group.