Miscellaneous
Timestamps
- class metricq.Timestamp(nanoseconds)
A MetricQ Timestamp
- Parameters:
- nanoseconds :
int
number of nanoseconds elapsed since the UNIX epoch
- nanoseconds :
- __add__(delta)
Return the timestamp delta in the future (or in the past, if delta is negative), relative to a
Timestamp
instance.>>> epoch = Timestamp(0) >>> a_week_later = epoch + Timedelta.from_string("7 days") >>> str(a_week_later) '[604800000000000] 1970-01-08 01:00:00+01:00'
- __eq__(other)
Check whether two
Timestamps
refer to the same instance of time:>>> now = Timestamp.now() >>> later = now + Timedelta.from_s(10) >>> now == later False
- __lt__(other)
Compare whether this timestamp describes a time before another timestamp.
>>> now = Timestamp.now() >>> later = now + Timedelta.from_s(10) >>> now < later True
Together with
__eq__()
, all relational operations (<=
,>
,!=
, etc.) are supported. Timestamps are totally ordered (as supplied byfunctools.total_ordering()
).
- __str__()
Yield a human-readable date-time string in the local timezone:
>>> # in UTC+01:00, it was already 1 in the night when the UNIX epoch happened :rtype: :py:class:`str`
>>> str(Timestamp(0)) '[0] 1970-01-01 01:00:00+01:00'
- classmethod ago(delta)
Return a timestamp delta in the past.
This is equivalent to:
Timestamp.now() - delta
- property datetime: datetime
Create an aware UTC datetime object
All MetricQ timestamps are POSIX timestamps, hence UTC.
- classmethod from_datetime(dt)
Create a Timestamp from an aware datetime object. If you have a naive datetime object, consider using
from_local_datetime()
instead.- Return type:
- classmethod from_iso8601(iso_string)
Create a Timestamp from a ISO 8601 date-time string.
>>> Timestamp.from_iso8601("1970-01-01T00:00:00.0Z") == Timestamp(0) True
This is a convenience method that parses the date-time string into a
datetime.datetime
usingdateutil.parser.isoparse()
, and then callsfrom_datetime()
to create aTimestamp
from that.The provided iso_string must include timezone information. To parse local time strings, you must convert them yourself and use
from_local_datetime()
. Or better yet, somehow create an awaredatetime.datetime
and usefrom_datetime()
.Note
The parser only supports up to 6 sub-second digits, further digits are simply dropped. If you need to parse timestamps with higher precision, you need to convert the string to nanoseconds yourself.
- classmethod from_local_datetime(dt)
Create a timestamp from a naive
datetime
object that uses the local timezone. If you have a proper aware object, usefrom_datetime()
instead.This function uses dateutil.tz.gettz() which uses /etc/localtime. Using naive datetime object can be error-prone. Naive local times are ambiguous during daylight savings time adjustments. If you have to use this function, your workflow is probably broken.
- Return type:
- classmethod from_now(delta)
Return a timestamp delta in the future.
This is equivalent to:
Timestamp.now() + delta
- classmethod from_posix_seconds(seconds)
Create a Timestamp from a POSIX timestamp
- classmethod now()
Return a Timestamp corresponding to “now”
Timedeltas (Durations)
- class metricq.Timedelta(nanoseconds)
A (possibly negative) duration of time
- Parameters:
- nanoseconds :
int
duration in nanoseconds
- nanoseconds :
- Supported operations:
Operation
Types
Result
d1 + d2
d1
,d2
:Timedelta
the sum of the duration of
d1
andd2
as aTimedelta
objectd + dt
d
:Timedelta
the combined duration of
d
andTimedelta.from_timedelta(dt)
as aTimedelta
objectd + t
a
Timestamp
offset by durationd
(the same ast + d
, seeTimestamp.__add__
)d1 == d2
d1
,d2
:Timedelta
True
ifd1
andd2
describe the same duration of timed == dt
d
:Timedelta
True
ifd.datetime == dt
(seedatetime.timedelta
)d1 < d2
d1
,d2
:Timedelta
True
ifd1
is shorter thand2
d < dt
d
:Timedelta
True
ifd.datetime < dt
(seedatetime.timedelta
)d * c
The duration scaled by the factor
c
, truncated to nanosecond precision See__mul__()
.d / c
The duration divided by
c
, truncated to nanosecond precision. See__truediv__()
.d // n
The duration divided by an integer factor
n
, truncated to nanosecond precision. See__floordiv__()
.In addition to
<
and=
, all other relational operations are supported and behave as you would expect. The type is hashable.
- __floordiv__(other)
Divide a duration by an integer or another
Timedelta
.>>> td = Timedelta.from_s(10) // 3 >>> td.s 3.333333333 :rtype: :py:data:`~typing.Union`\[:py:class:`~metricq.timeseries.timedelta.Timedelta`, :py:class:`int`]
>>> td.precise_string '3333333333ns'
This divides the number of nanoseconds in this duration by factor using Floor Division, i.e. for some
n: int
andtd: Timedelta
we have>>> assert (td // n).ns == td.ns // n
Division by another
Timedelta
yields an integral result:>>> assert Timedelta.from_s(10) // Timedelta.from_s(3) == 3
Note
Floor Division produces surprising results for
float
arguments and is therefore not supported here (i.e.3 // 0.2 == 14.0
). Use__truediv__()
if you want to scale a duration by a non-integer factor.
- __str__()
A string containing the number of seconds of this duration
- Return type:
>>> "One day has {} seconds".format(Timedelta.from_string("1 day")) 'One day has 86400.0s seconds'
- __truediv__(other)
Divide a duration by a floating point or
Timedelta
.>>> (Timedelta.from_s(10) / 2.5).precise_string '4s'
This divides the number of nanoseconds in this duration by factor using True Division, but truncates the result to an
int
:>>> assert (td / x).ns == int(td.ns / x)
where
td: Timedelta
andx: float
.Division by another
Timedelta
instead yields afloat
result.Note
Use
__floordiv__()
if you know that the divisor is an integer, this will prevent rounding errors.
- staticmethod from_ms(milliseconds)
Create a duration from a number of milliseconds
- staticmethod from_s(seconds)
Create a duration from a number of seconds
- staticmethod from_string(duration_str)
Parse a human-readable string representation of a duration.
>>> "One day has {} seconds".format(Timedelta.from_string("1 day")) 'One day has 86400.0s seconds'
- Parameters:
- duration_str :
str
A duration string in the form of “<number> <unit>”. Accepted units are:
Name
short
long
Seconds
"s"
"second"
,"seconds"
Milliseconds
"ms"
"millisecond"
,"milliseconds"
Microseconds
"us"
"microsecond"
,"microseconds"
Nanoseconds
"ns"
"nanosecond"
,"nanoseconds"
Minutes
"min"
"minute"
,"minutes"
Hours
"h"
"hour"
,"hours"
Days
"d"
"day"
,"days"
- duration_str :
- Return type:
- Returns:
The parsed
Timedelta
object- Raises:
ValueError – if the string is not of the correct format or an invalid unit was specified
- staticmethod from_timedelta(delta)
Convert from a standard
timedelta
object.- Parameters:
- delta :
timedelta
a standard
datetime.timedelta
object
- delta :
- Return type:
- Returns:
A
Timedelta
object
- staticmethod from_us(microseconds)
Create a duration from a number of microseconds
- property precise_string: str
An exact string representation of the duration preserving all nanosecond digits. If possible, a human-readable string is returned, e.g.
100ms
. The result of this can be parsed byfrom_string()
without losing precision.
- property timedelta: timedelta
This duration as a standard
datetime.timedelta
object
Time-value pairs and aggregates
- class metricq.TimeValue(timestamp, value)
A timestamp-value pair.
Unpack it like so:
>>> tv = TimeValue(Timestamp.now(), 42.0) >>> (timestamp, value) = tv
- class metricq.TimeAggregate(timestamp, minimum, maximum, sum, count, integral_ns, active_time)
Summary of a metric’s values within a certain period of time.
- staticmethod from_proto(timestamp, proto)
- Return type:
- staticmethod from_value(timestamp, value)
- Return type:
- staticmethod from_value_pair(timestamp_before, timestamp, value)
Create a TimeAggregate from a pair of timestamps (class:Timestamp) and one value
- Raises:
NonMonotonicTimestamps – if the two timestamps are not strictly monotonic
- Return type:
- property integral_s: float
Integral of values in this aggregate over its active time, seconds-based
- property mean: float
Mean value of this aggregate. This is the general way to access the mean value if nothing specific is known about the underlying raw data.
It may involve a heuristic to decide between
mean_integral
andmean_sum
. Currently, it defaults tomean_integral
.
- property mean_integral: float
Mean value of this aggregate, calculated from the integral. Use this if you want to explicitly force this property.
In the HTA context, this should only be NaN if the aggregate interval is outside the of the interval from the earliest to the latest measurement point.
- property mean_sum: float
Mean value of this aggregate, calculated from the sum. This can be useful when the underlying metric should be at regular intervals, but there are gaps in the data. Otherwise,
mean_integral
or justmean
are more appropriate.This value will be NaN if there are no raw data points in the aggregate interval.
Helper types
RPC handling
- @metricq.rpc_handler(*function_tags)
A Decorator to mark an
async
method as an RPC handler- Parameters:
- function_tags :
str
The names of the RPCs that this method should handle
- function_tags :
- Return type:
Callable
[[Callable
[...
,Optional
[Any
]]],Callable
[...
,Optional
[Any
]]]
Example
from metricq import Source, rpc_handler class MySource(Source): ... @rpc_handler("config") async def on_config(self, **config): print(f"Received configuration: {config}") ...
Note
This only has an effect on methods of classes implementing MetricQ clients (see
Client
), i.e.Sink
,Source
,IntervalSource
,HistoryClient
, etc.