MetricService
Expose TM1 model-performance statistics uniformly across v11 and v12.
This module holds both the :class:MetricService (version dispatch + REST I/O)
and the pure, server-free helpers it delegates to: the v11 measure vocabulary,
the v11 MDX builders, the v12 Metrics() $filter builder, and the record
shapers. Keeping the helpers at module scope lets them be unit-tested without a
live TM1 server while the service stays a thin orchestrator.
One method per Stats Category (by_cube, by_server, ...). Each method
returns the same shape regardless of the underlying TM1 version, hiding whether
the data came from a v11 }Stats* control cube (MDX/cellset) or the v12
Metrics() OData endpoint. Reads never mutate server state. Two record
orientations:
- gauge-long (
by_cube,by_server): one row per metric, withMetric/Value/Unit. - entity-wide (
by_ruleand the v11-only entity categories): one row per entity with heterogeneous attribute columns.
Values are passed through verbatim — never converted. Read Unit to interpret
them (v11 memory is raw bytes; v12 reports its own unit).
ALL_TIME_INTERVALS = 'ALL'
module-attribute
CATEGORY_BY_CHORE = 'by_chore'
module-attribute
CATEGORY_BY_CLIENT = 'by_client'
module-attribute
CATEGORY_BY_CUBE = 'by_cube'
module-attribute
CATEGORY_BY_CUBE_BY_CLIENT = 'by_cube_by_client'
module-attribute
CATEGORY_BY_PROCESS = 'by_process'
module-attribute
CATEGORY_BY_RULE = 'by_rule'
module-attribute
CATEGORY_BY_SERVER = 'by_server'
module-attribute
CUBES_TOTAL = 'Cubes Total'
module-attribute
DEFAULT_TIME_INTERVAL = 'LATEST'
module-attribute
ENTITY_DIM_COLUMN = {'}Cubes': 'CubeName', '}PerfCubes': 'CubeName', '}LineNumber': 'LineNumber', '}Processes': 'ProcessName', '}Chores': 'ChoreName', '}PerfClients': 'ClientName', '}Cube Functions': 'CubeFunction'}
module-attribute
ENTITY_MEASURE_COLUMNS = {CATEGORY_BY_RULE: {'Rule Text': 'RuleText', 'Total Run Count': 'TotalRunCount', 'Min Time (ms)': 'MinTimeMs', 'Max Time (ms)': 'MaxTimeMs', 'Avg Time (ms)': 'AvgTimeMs', 'Total Time (ms)': 'TotalTimeMs', 'Last Run Time': 'LastRunTime'}, CATEGORY_BY_PROCESS: {'Current State': 'CurrentState', 'Completion Status': 'CompletionStatus', 'Client Name': 'ClientName', 'Last Start Time': 'LastStartTime', 'Last End Time': 'LastEndTime', 'Last Duration': 'LastDuration', 'Next Activation Time': 'NextActivationTime', 'Current Process': 'CurrentProcess'}, CATEGORY_BY_CLIENT: {'Message Count': 'MessageCount', 'Message Bytes': 'MessageBytes', 'Request Count': 'RequestCount', 'Elapse Time (ms)': 'ElapseTimeMs', 'Bytes/Message': 'BytesPerMessage'}, CATEGORY_BY_CUBE_BY_CLIENT: {'Count': 'Count', 'Elapse Time (ms)': 'ElapseTimeMs'}}
module-attribute
UNIT_BYTES = 'B'
module-attribute
UNIT_COUNT = '#'
module-attribute
V12_VERSION = '12.0.0'
module-attribute
MetricService(rest)
Bases: ObjectService
Expose TM1 model-performance statistics uniformly across v11 and v12.
One method per Stats Category (by_cube, by_server, ...). Each method
returns the same shape regardless of the underlying TM1 version, hiding
whether the data came from a v11 }Stats* control cube (MDX/cellset) or
the v12 Metrics() OData endpoint. Reads never mutate server state.
Two return orientations, chosen by the data's nature:
- gauge-long (
by_cube,by_server): one row per metric, withMetric/Value/Unit. - entity-wide (
by_ruleand the v11-only categories): one row per entity with attribute columns.
Every read method has a parallel *_as_dataframe variant.
Per-cube metrics (gauge-long), unified across versions::
>>> rows = tm1.metrics.by_cube(cube="plan_BudgetPlan")
>>> rows[0]
{'Category': 'by_cube', 'CubeName': 'plan_BudgetPlan',
'Metric': 'cube_memory_used', 'NativeName': 'Total Memory Used',
'Value': 8385536, 'Unit': 'B', 'ReplicaID': 0,
'TimeInterval': 'LATEST', 'Timestamp': None}
The canonical Metric name is stable across versions; NativeName
carries the source's own name. Values pass through unconverted — read
Unit to interpret them (cube_memory_used is bytes on v11, KB on v12).
As a filtered DataFrame::
>>> df = tm1.metrics.by_cube_as_dataframe(metrics=["cube_memory_used"])
Server/replica-level metrics. v12 (highly-available) yields one row per
replica; v11 is a single replica (ReplicaID=0)::
>>> tm1.metrics.by_server(metrics=["replica_memory_used"])
Per-rule timing (entity-wide), unified across versions — the read is
identical; only how }StatsByRule gets populated differs. On v11 the
Performance Monitor populates it, so ensure it is running, then read::
>>> tm1.metrics.get_performance_monitor_state()
False
>>> tm1.metrics.start_performance_monitor()
>>> tm1.metrics.by_rule(cube="plan_BudgetPlan")
On v12 collect on demand: start, exercise the cube's rules, let the ~60s sampling interval elapse, flush, then read the same way::
>>> tm1.metrics.start_collecting_rule_stats("plan_BudgetPlan")
>>> # ... exercise the cube's rules, wait ~60s for the sampling interval ...
>>> tm1.metrics.flush_collected_rule_stats("plan_BudgetPlan")
>>> tm1.metrics.stop_collecting_rule_stats("plan_BudgetPlan")
>>> tm1.metrics.by_rule(cube="plan_BudgetPlan")
v11-only categories (the cubes were removed in v12; these raise
:class:TM1pyVersionException on a v12 database)::
>>> tm1.metrics.by_process()
>>> tm1.metrics.by_chore()
>>> tm1.metrics.by_client()
>>> tm1.metrics.by_cube_by_client(cube="plan_BudgetPlan")
Two version-error flavours are used deliberately. Version-gated reads —
the v11-only categories above and the v11-only time_interval / v12-only
since arguments — raise :class:TM1pyVersionException (the underlying
source simply does not exist on the other version). The Performance Monitor
controls (:meth:start_performance_monitor etc.) raise
:class:TM1pyVersionDeprecationException, mirroring the deprecated
:class:ServerService methods they delegate to.
Source code in TM1py/Services/MetricService.py
STATS_BY_RULE_CUBE = '}StatsByRule'
class-attribute
instance-attribute
by_chore(**kwargs)
Per-chore execution statistics (entity-wide). v11 only.
Raises:
| Type | Description |
|---|---|
TM1pyVersionException
|
on v12 (the |
Source code in TM1py/Services/MetricService.py
by_chore_as_dataframe(*args, **kwargs)
by_client(**kwargs)
Per-client request/message statistics (entity-wide). v11 only.
Raises:
| Type | Description |
|---|---|
TM1pyVersionException
|
on v12 (the |
Source code in TM1py/Services/MetricService.py
by_client_as_dataframe(*args, **kwargs)
by_cube(cube=None, metrics=None, time_interval=None, since=None, include_control=False, **kwargs)
Per-cube performance metrics (gauge-long), unified across versions.
}-control cubes and the synthetic Cubes Total row are excluded
by default; include_control=True adds }-cubes (v11 only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_interval
|
str
|
v11-only rolling-window selector (see
:meth: |
None
|
since
|
datetime
|
v12-only — return only metrics whose |
None
|
Source code in TM1py/Services/MetricService.py
by_cube_as_dataframe(*args, **kwargs)
by_cube_by_client(cube=None, **kwargs)
Per-cube-per-client access statistics (entity-wide). v11 only.
Raises:
| Type | Description |
|---|---|
TM1pyVersionException
|
on v12 (the |
Source code in TM1py/Services/MetricService.py
by_cube_by_client_as_dataframe(*args, **kwargs)
by_process(**kwargs)
Per-process execution statistics (entity-wide). v11 only.
Raises:
| Type | Description |
|---|---|
TM1pyVersionException
|
on v12 (the |
Source code in TM1py/Services/MetricService.py
by_process_as_dataframe(*args, **kwargs)
by_rule(cube=None, **kwargs)
Per-rule-line statistics (entity-wide), unified across versions.
}StatsByRule is structurally identical on v11 and v12 (dimensions
}Cubes x }LineNumber x }RuleStats), so the same cellset
read/shape path serves both. If the cube does not exist yet, this returns
[] with a warning rather than raising.
How the cube gets populated differs by version: on v11 the Performance
Monitor populates it. On v12 it is created/updated on demand by the
rule-stats collection lifecycle — call :meth:start_collecting_rule_stats,
exercise the cube's rules, allow the ~60s sampling interval to elapse,
then :meth:flush_collected_rule_stats (verified on 12.5.9).
Source code in TM1py/Services/MetricService.py
by_rule_as_dataframe(*args, **kwargs)
by_server(metrics=None, time_interval=None, since=None, **kwargs)
Server/replica-level metrics (gauge-long), unified across versions.
Always one row per replica with a ReplicaID column (v11 -> 0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_interval
|
str
|
v11-only rolling-window selector; passing it on
v12 raises :class: |
None
|
since
|
datetime
|
v12-only |
None
|
Source code in TM1py/Services/MetricService.py
by_server_as_dataframe(*args, **kwargs)
flush_collected_rule_stats(cube, **kwargs)
Flush the rule stats collected for cube into }StatsByRule (v12).
Writes the rule statistics collected since :meth:start_collecting_rule_stats
into the }StatsByRule cube (created on demand), which :meth:by_rule
then reads. Collection is sampled on a ~60s interval, so exercise the
cube's rules and let that interval elapse between start and flush for
stats to appear (verified on 12.5.9).
Source code in TM1py/Services/MetricService.py
get_performance_monitor_state()
Return whether the Performance Monitor is currently active (v11 only).
Raises :class:TM1pyVersionDeprecationException on v12. Delegates to
:meth:ServerService.get_performance_monitor_state.
Source code in TM1py/Services/MetricService.py
start_collecting_rule_stats(cube, **kwargs)
Start collecting per-rule timing statistics for cube (v12).
Source code in TM1py/Services/MetricService.py
start_performance_monitor()
Turn the Performance Monitor on (v11 only).
While it runs, TM1 populates the }Stats* cubes that :meth:by_cube,
:meth:by_server, and :meth:by_rule read on v11. PerformanceMonitorOn
is a v11-only setting, so this raises :class:TM1pyVersionDeprecationException
on v12. Delegates to :meth:ServerService.start_performance_monitor.
Source code in TM1py/Services/MetricService.py
stop_collecting_rule_stats(cube, **kwargs)
Stop collecting per-rule timing statistics for cube (v12).
Source code in TM1py/Services/MetricService.py
stop_performance_monitor()
Turn the Performance Monitor off (v11 only).
Raises :class:TM1pyVersionDeprecationException on v12. Delegates to
:meth:ServerService.stop_performance_monitor.
Source code in TM1py/Services/MetricService.py
build_metrics_url(cube_name=None, metrics=None, timestamp=None)
Build the relative /Metrics() URL, optionally with a $filter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cube_name
|
str
|
restrict to a single cube ( |
None
|
metrics
|
Optional[List[str]]
|
restrict to these canonical metric names
( |
None
|
timestamp
|
datetime
|
only metrics newer than this ( |
None
|
Returns:
| Type | Description |
|---|---|
str
|
|
Source code in TM1py/Services/MetricService.py
build_v11_entity_mdx(category, cube=None)
Build the v11 MDX for an entity (wide) category.
Measures (the category's measure dimension) go on axis 0; the entity
dimension(s) are crossjoined NON EMPTY on axis 1; a LATEST slicer is
added when the cube has a time dimension. cube restricts the relevant
entity dimension to a single member.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
build_v11_mdx(category, cube=None, time_interval=None, include_control=False)
Build the v11 MDX for a gauge category.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
category
|
str
|
|
required |
cube
|
str
|
restrict to a single entity ( |
None
|
time_interval
|
str
|
|
None
|
include_control
|
bool
|
include |
False
|
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
entity_measure_columns(category)
Return the native-measure -> column-name map for an entity category.
Source code in TM1py/Services/MetricService.py
normalize_v11_measure(category, native_name)
Normalize a raw v11 measure into (Metric, NativeName, Unit).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
category
|
str
|
a gauge Stats Category ( |
required |
native_name
|
str
|
the v11 measure name as reported by the |
required |
Returns:
| Type | Description |
|---|---|
Tuple[str, str, Optional[str]]
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
if the category is not a known gauge category, or the measure is not in that category's mapping table. |
Source code in TM1py/Services/MetricService.py
shape_v11_entity_records(cellset, category)
Shape a raw v11 }Stats* cellset into entity-wide records for category.
One row per entity (e.g. per (CubeName, LineNumber) for by_rule);
each of the category's measures becomes a column. Members are mapped to
their dimension via UniqueName. ReplicaID is always 0 on v11.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
shape_v11_gauge_records(cellset, category)
Shape a raw v11 }Stats* cellset into gauge-long records for category.
Members are mapped to their dimension via UniqueName (the context/time
axis ordinal is not fixed across queries), then each cell becomes one
record: the measure is normalized to its canonical Metric/Unit via
the vocabulary, the entity (cube) and time bucket are read from their
dimensions. ReplicaID is always 0 on v11. Values are passed through
verbatim (including None).
Note: v11 surfaces the MDX WHERE slicer as an axis in the cellset
response (verified on a live 11.8 server), so the }TimeIntervals bucket
— LATEST, a specific bucket, or the full window — is always present on
an axis and read per-row. DEFAULT_TIME_INTERVAL is only a safety
fallback for the (unobserved) case where it is absent.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
shape_v12_gauge_records(raw, category)
Shape raw v12 Metrics() rows into gauge-long records for category.
Selects only the rows whose Name belongs to the category
(cube_* for by_cube, replica_* for by_server). On v12 the
metric name is canonical verbatim, so NativeName == Metric. CubeName
is included for by_cube only.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
v11_entity_spec(category)
Return a copy of the v11 cube/dimension spec for an entity category.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
Source code in TM1py/Services/MetricService.py
v11_measure_names(category)
Return the v11 native measure names for a gauge category, in canonical order.
Used by the v11 MDX builder to select exactly the measures TM1py maps.
Source code in TM1py/Services/MetricService.py
v11_spec(category)
Return a copy of the v11 cube/dimension spec for a gauge category.
Used by the record shaper to map cellset members to their dimension roles.
Raises:
| Type | Description |
|---|---|
KeyError
|
if |