Appearance
Send Payload Logs and Metrics
This example runs on a payload that flies on a Husky bus. It sends a temperature gauge, a dropped-frames counter and a warning line to the bus with PayloadTelemetry. The bus labels them with your payload and forwards them to Urban Sky.
Use it for diagnostics. Values that operators must see in Mission Control go through telemetry instead.
Before you start
- Connect the payload to the bus's payload LAN and get an address by DHCP.
- Declare your metrics: in your
:8090capability document, or withdeclare_metrics()/declareMetrics()if your payload has none. This example usestemp_c(GAUGE, attributesensorwith valuecpu) andframes_dropped(COUNTER). The bus drops undeclared metric values and counts them asundeclared. - Copy the client file into your project. It has no dependencies and needs no API token.
bash
# Python 3.8+
SDK_VERSION=$(curl -fsSL https://sdk.atmosys.com/runtime/py/current/version.txt)
curl -fsSL "https://sdk.atmosys.com/runtime/py/${SDK_VERSION}/urbansky_sdk/payload.py" -o urbansky_payload.py
# Node.js 18+
SDK_VERSION=$(curl -fsSL https://sdk.atmosys.com/runtime/js/current/version.txt)
curl -fsSL "https://sdk.atmosys.com/runtime/js/${SDK_VERSION}/payload-telemetry.js" -o urbansky-payload.jsPython
py
#!/usr/bin/env python3
"""Run on a payload: send logs and metrics to the Husky bus.
Put urbansky_payload.py beside this file first (see the Python reference), then:
python payload-telemetry-example.py
The client finds the bus itself (set USKY_BUS_INGEST_URL to override). A payload
with a :8090 capability document declares its metrics there; this example
declares them with declare_metrics() instead.
"""
import logging
import random
import time
try:
from urbansky_payload import PayloadLogHandler, PayloadTelemetry
except ImportError: # running from the SDK source tree
from urbansky_sdk.payload import PayloadLogHandler, PayloadTelemetry
log = logging.getLogger("camera")
log.setLevel(logging.INFO)
METRICS = [
{
"name": "temp_c",
"type": "GAUGE",
"unit": "Cel",
"attributes": [{"key": "sensor", "values": ["cpu"]}],
},
{"name": "frames_dropped", "type": "COUNTER"},
]
with PayloadTelemetry() as telemetry:
telemetry.declare_metrics(METRICS)
log.addHandler(PayloadLogHandler(telemetry))
print("bus says:", telemetry.status())
for _ in range(30):
temp = 50 + random.random() * 10
telemetry.gauge("temp_c", temp, sensor="cpu")
if random.random() < 0.2:
telemetry.count("frames_dropped")
log.warning(
"frame drop",
extra={"event": "camera_frame_drop", "attrs": {"stream": "eo"}},
)
time.sleep(1)
print(
"dropped on the payload side:",
telemetry.dropped,
"last reply:",
telemetry.last_result,
)JavaScript
js
// Run on a payload: send logs and metrics to the Husky bus.
//
// Put urbansky-payload.js beside this file first (see the JavaScript
// reference), then:
//
// node payload-telemetry-example.js
//
// The client finds the bus itself (set USKY_BUS_INGEST_URL to override). A
// payload with a :8090 capability document declares its metrics there; this
// example declares them with declareMetrics() instead.
const { PayloadTelemetry } = require('./urbansky-payload.js')
async function main() {
const telemetry = new PayloadTelemetry()
telemetry.declareMetrics([
{
name: 'temp_c',
type: 'GAUGE',
unit: 'Cel',
attributes: [{ key: 'sensor', values: ['cpu'] }],
},
{ name: 'frames_dropped', type: 'COUNTER' },
])
console.log('bus says:', await telemetry.status())
for (let i = 0; i < 30; i++) {
telemetry.gauge('temp_c', 50 + Math.random() * 10, { sensor: 'cpu' })
if (Math.random() < 0.2) {
telemetry.count('frames_dropped')
telemetry.warn('frame drop', { event: 'camera_frame_drop', attrs: { stream: 'eo' } })
}
await new Promise(resolve => setTimeout(resolve, 1000))
}
await telemetry.close()
console.log(
'dropped on the payload side:',
telemetry.dropped,
'last reply:',
telemetry.lastResult,
)
}
main()Run it
bash
python payload-telemetry-example.py
# or
node payload-telemetry-example.jsThe client finds the bus on its own (192.168.8.1 on a Husky bus, 10.55.0.1 on a cloud bench bus). Set USKY_BUS_INGEST_URL to use another address.
Check that it worked
bus says:prints the bus's view of your payload:payload.id,payload.kind, the admitted and refused metrics, and drop counts.Noneornullmeans the bus did not answer. Check the address and the cable.last reply:is the bus's last answer, for example{"accepted": 1, "dropped": 0, "reasons": {}}.reasons.undeclaredcounts metric values for names or attributes that are not declared. Declare them (step 2).dropped on the payload sidecounts lines the client dropped. It stays at 0 unless the bus is unreachable for a long time or refuses a request.
Next steps
- Python reference and JavaScript reference: every option, the queue, pacing and drops.
- Send logs and metrics: routes, limits, and how to log so that your lines survive the scrub.