Logging Conditions¶
Logging conditions allow you to control when trace events are logged, reducing data volume and improving performance by only logging when specific criteria are met.
Quick Start¶
Enable basic filtering with a single line by setting a default condition across all signals:
import zelos_sdk
zelos_sdk.init()
source = zelos_sdk.TraceSourceCacheLast("my_source")
# Enable smart logging: log changes immediately, or every 1 second if no changes
source.set_default_log_condition()
# Define your schema
schema = [
zelos_sdk.TraceEventFieldMetadata("temperature", zelos_sdk.DataType.Float64, "°C"),
zelos_sdk.TraceEventFieldMetadata("status", zelos_sdk.DataType.String),
]
event = source.add_event("sensor_reading", schema)
# Only logs when temperature changes by sys.float_info.epsilon or status changes
# Also logs at 1Hz intervals regardless of changes
source.log("sensor_reading", {"temperature": 25.0, "status": "normal"})
source.log("sensor_reading", {"temperature": 25.0, "status": "normal"}) # Won't log (no changes)
source.log("sensor_reading", {"temperature": 26.0, "status": "normal"}) # Logs (temperature changed)
Explicit, Custom Conditions¶
Custom Default Condition¶
Set a custom default condition that applies to fields without explicit conditions:
import zelos_sdk
zelos_sdk.init()
source = zelos_sdk.TraceSourceCacheLast("motor_controller")
# Custom default: more sensitive to changes, longer time intervals
custom_default = zelos_sdk.trace.conditions.DeltaOrTimeLogCondition(
delta=0.1, # Send logs when values change by 0.1
time_threshold_s=60.0 # Send logs every minute
)
source.set_default_log_condition(custom_default)
schema = [
zelos_sdk.TraceEventFieldMetadata("rpm", zelos_sdk.DataType.Float64),
zelos_sdk.TraceEventFieldMetadata("torque", zelos_sdk.DataType.Float64, "Nm"),
zelos_sdk.TraceEventFieldMetadata("mode", zelos_sdk.DataType.String),
]
# Mix explicit and default conditions
conditions = {
"rpm": zelos_sdk.trace.conditions.DeltaLogCondition(delta=100.0), # Explicit: log RPM changes ≥ 100
"torque": None, # Always log torque unconditionally, do not use default
# 'mode' will use the custom default condition
}
event = source.add_event("motor_stats", schema, conditions)
Core Concepts¶
TraceSourceCacheLast¶
Unlike the basic TraceSource
, TraceSourceCacheLast
caches field values and supports conditional logging:
# Basic source - logs everything
basic_source = zelos_sdk.TraceSource("basic")
# Cached source - supports conditional logging
cached_source = zelos_sdk.TraceSourceCacheLast("cached")
Condition Types¶
ValueLogCondition¶
Logs when the value changes from the last logged value:
condition = zelos_sdk.ValueLogCondition()
# First log: True (no previous value)
# Log "hello": True
# Log "hello": False (same value)
# Log "world": True (value changed)
EpsilonLogCondition¶
Logs when numeric values change by more than epsilon:
condition = zelos_sdk.EpsilonLogCondition(epsilon=0.5)
# Log 10.0: True (first log)
# Log 10.3: False (change = 0.3 < 0.5)
# Log 10.8: True (change = 0.8 > 0.5)
TimeLogCondition¶
Logs based purely on time intervals:
condition = zelos_sdk.TimeLogCondition(time_threshold_s=1.0)
# Logs every 1 second regardless of value changes
DefaultLogCondition¶
Smart default that combines time + value/epsilon logic:
condition = zelos_sdk.DefaultLogCondition(
time_threshold_s=1.0, # Log at least every 1 second
epsilon=0.1 # For floats, log if change > 0.1
)
# For float values: logs if change > epsilon OR time > 1s
# For non-float values: logs if value changes OR time > 1s
Composite Conditions¶
Combine conditions with OR logic:
# Log when value changes OR every 30 seconds
condition = zelos_sdk.ValueOrTimeLogCondition(time_threshold_s=30.0)
# Log when change exceeds epsilon OR every 10 seconds
condition = zelos_sdk.EpsilonOrTimeLogCondition(epsilon=1.0, time_threshold_s=10.0)
# Log when change exceeds delta OR every 5 seconds
condition = zelos_sdk.DeltaOrTimeLogCondition(delta=5.0, time_threshold_s=5.0)
Field-Level Conditions¶
Apply different conditions to different fields:
source = zelos_sdk.TraceSourceCacheLast("mixed_sensor")
schema = [
zelos_sdk.TraceEventFieldMetadata("temperature", zelos_sdk.DataType.Float64),
zelos_sdk.TraceEventFieldMetadata("pressure", zelos_sdk.DataType.Float64),
zelos_sdk.TraceEventFieldMetadata("status", zelos_sdk.DataType.String),
]
# Different conditions per field
conditions = {
"temperature": zelos_sdk.EpsilonLogCondition(epsilon=0.5), # ±0.5°C threshold
"pressure": zelos_sdk.DeltaLogCondition(delta=10.0), # ±10 Pa threshold
"status": zelos_sdk.ValueLogCondition(), # Log any status change
}
event = source.add_event("readings", schema, conditions)
# Each field is evaluated independently
source.log("readings", {
"temperature": 25.0, # Logs if change > 0.5°C from last logged temp
"pressure": 1013.0, # Logs if change > 10 Pa from last logged pressure
"status": "normal" # Logs if different from last logged status
})
Advanced Usage¶
Preventing Value Drift¶
DeltaLogCondition
prevents gradual drift by comparing against the last logged value, not the last received value:
condition = zelos_sdk.DeltaLogCondition(delta=10.0)
# Last logged: 100.0
# Current: 105.0 → No log (change = 5.0 < 10.0)
# Current: 108.0 → No log (change from 100.0 = 8.0 < 10.0)
# Current: 111.0 → Log! (change from 100.0 = 11.0 > 10.0)
Caching vs Logging¶
Values are always cached, even when not logged:
source = zelos_sdk.TraceSourceCacheLast("cache_demo")
schema = [zelos_sdk.TraceEventFieldMetadata("value", zelos_sdk.DataType.Float64)]
event = source.add_event("test", schema, {"value": zelos_sdk.ValueLogCondition()})
source.log("test", {"value": 42.0}) # Logs and caches
source.log("test", {"value": 42.0}) # Only caches (condition not met)
# Value is still accessible
print(event.value.get()) # Returns: 42.0
Custom Condition Combinations¶
Build complex logic with CompositeLogCondition
:
# Log if ANY of these conditions are met
complex_condition = zelos_sdk.CompositeLogCondition([
zelos_sdk.EpsilonLogCondition(epsilon=0.1), # Small changes
zelos_sdk.TimeLogCondition(time_threshold_s=60), # Every minute
zelos_sdk.ValueLogCondition() # Any change (for non-numeric)
])
Performance Considerations¶
- Minimal overhead: Conditions are evaluated in-memory before network transmission
- Caching:
TraceSourceCacheLast
maintains field values locally for fast comparison - Batching: Filtered events are still batched for efficient transmission
- Memory usage: Each field stores its last logged value and timestamp
Common Patterns¶
High-frequency sensor data¶
# Log significant changes immediately, heartbeat every 10s
source.set_default_log_condition(
zelos_sdk.EpsilonOrTimeLogCondition(epsilon=0.5, time_threshold_s=10.0)
)
Status monitoring¶
# Log all status changes, periodic heartbeat
conditions = {
"status": zelos_sdk.ValueOrTimeLogCondition(time_threshold_s=30.0),
"error_count": zelos_sdk.ValueLogCondition(), # Log every error count change
}
Battery monitoring¶
# Sensitive to voltage changes, less sensitive to current
conditions = {
"voltage": zelos_sdk.EpsilonLogCondition(epsilon=0.1), # ±0.1V
"current": zelos_sdk.DeltaLogCondition(delta=50.0), # ±50mA
}