Extra log fields
A bare log message often isn't enough to diagnose a problem: which valve, at what pressure, on which orbit? Every subsystem wants to attach different details, and nobody wants a format_log() with fifty optional parameters.
Let the function take whatever fields the caller has, then feed it fields from dicts.
Finish stage 1 to unlock this stage.
Let format_log() take any extra keyword arguments and add each one to the end of the line as a space and key=value, in the order they were passed:
[T+00:12:05] INFO Engines nominal [T+00:14:47] ERROR Fuel valve 2 not responding valve=2 pressure_kpa=310
Add **fields after msg. Inside, fields is a dict: build the line, then loop over fields.items() and add to it.
Finish stage 1 to unlock this stage.
The fields often come ready in dicts. Pass them as keyword arguments with **: first reading alone, then reading and context together:
[T+00:16:03] WARN Engine hot sensor=ENG-2 temperature_c=455.2 [T+00:16:03] WARN Engine hot sensor=ENG-2 temperature_c=455.2 orbit=12 station=GS-2
format_log(..., **reading) works as if you had written sensor="ENG-2", temperature_c=455.2 yourself.
Hints
Variable Keyword Arguments **kwargs
# Two stars collect every extra named argument into a dictionary
def telemetry(craft, **readings):
print("Packet from", craft)
print("Collected as:", readings)
for sensor, value in readings.items():
print(" ", sensor, "=", value)
print("Battery:", readings.get("battery_pct", "not reported"))
print()
# Any names you like, and they arrive as keys
telemetry("Cassini", battery_pct=71, temp_c=-23)
# A different craft reports completely different sensors
telemetry("InSight", quakes_detected=1319, dust_opacity=0.68, sol=1440)
# None at all, and readings is simply an empty dictionary
telemetry("Akatsuki")
# The name kwargs is only a convention, the stars do the work
def show(**whatever):
print(whatever)
show(alpha=1, beta=2)
# Two stars at a call spread a dictionary back into named arguments
packet = {"battery_pct": 88, "signal_db": -140}
telemetry("Solar Orbiter", **packet)
Separate arguments with commas. They're passed on every Run as sys.argv[1:].