import logging import universal_tsdb as utsdb from universal_tsdb import Client, MaxErrorsException import functools import time __all__ = ["Client", "Ingester", "MaxErrorsException"] logging.basicConfig( format="%(asctime)s %(levelname)8s %(name)s: %(message)s", datefmt="%m/%d/%Y %H:%M:%S", ) logger = logging.getLogger("qolab.tsdb") logger.setLevel(logging.INFO) class Ingester(utsdb.Ingester): """Same as universal_tsdb.Ingester but sets measurement_prefix. so measurement becomes measurement_prefix.measurement""" def __init__(self, client, batch=0, measurement_prefix=""): super().__init__(client, batch=batch) self.measurement_prefix = measurement_prefix def append(self, timestamp=None, tags=None, measurement=None, **kwargs): if self.measurement_prefix is None or not isinstance( self.measurement_prefix, str ): raise ValueError("Invalid measurement_prefix, it should be string") if measurement is None or not isinstance(measurement, str): raise ValueError("Invalid measurement, it should be string") qolab_measurement = ".".join((self.measurement_prefix, measurement)) # space is illegal for measurements fields qolab_measurement = qolab_measurement.replace(" ", "-") logger.debug(f"{qolab_measurement=} {tags=}, {kwargs=}") return super().append( timestamp=timestamp, tags=tags, measurement=qolab_measurement, **kwargs ) def tsdb_append_metric_for_class_setter_or_getter(tsdb_logger=None): def wrap(f): @functools.wraps(f) def wrapper(*args, **kwds): if f.__name__[0:3] != "get" and f.__name__[0:3] != "set": logger.warning( f"Do not know how to work with {f.__name__}, it is neither set... or get..." ) ret = f(*args, **kwds) return ret cls = args[0] action = f.__name__[0:3] var_name = f.__name__[3:] val = None if cls.config["DeviceNickname"] is not None: device_type = cls.config["DeviceNickname"] else: device_type = cls.config["Device type"] if action == "get": """getter""" val = f(*args, **kwds) ts = time.time() ret = val else: """setter""" val = args[1] ts = time.time() ret = f(*args, **kwds) logger.debug(f"function {f.__name__} {action} {var_name} = {val}") ts_ms = int(ts * 1000) fields = {var_name: val} try: if cls.tsdb_ingester is not None: cls.tsdb_ingester.append( ts_ms, measurement=device_type, tags={"action": action}, **fields, ) except ValueError as err: logger.error(f"{err=} in function {f.__name__}: {var_name} = {val}") return ret return wrapper return wrap if __name__ == "__main__": from qolab.hardware.basic import BasicInstrument tsdb_client = Client("influx", "http://localhost:8428", database="qolab") tsdb_ingester = Ingester( tsdb_client, batch=10, measurement_prefix="experiment.title" ) class InstWithLog(BasicInstrument): def __init__(self, *args, **kwds): super().__init__(*args, **kwds) self.config["Device type"] = "TestTSDBLogger" self.config["Device model"] = "v01" self.config["FnamePrefix"] = "test_log" self.config["SavePath"] = "./data" self.deviceProperties.update({"D"}) self.d = 13.45 @BasicInstrument.tsdb_append def setD(self, val): self.d = val @BasicInstrument.tsdb_append def getD(self): """get D variable""" return self.d dev = InstWithLog(tsdb_ingester=tsdb_ingester, device_nickname="tester") dev.getD() dev.setD(3) tsdb_ingester.commit()