Bittensor API Reference
bt.subtensor
The Subtensor
is utilized for managing interactions with the subtensor chain. It serves as an interface to communicate with "Finney", Bittensor's main blockchain network, or others, enabling operations like querying and transacting.
Examples
# Creating a default chain connection to remote finney instance.
sub = bt.subtensor()
# Parsing --subtensor.network and --subtensor.chain_endpoint from the command line
sub = bt.subtensor( config = bt.subtensor.config() )
# Connecting subtensor's default local entrypoint "ws://127.0.0.1:9944"
sub = bt.subtensor( network = 'local' )
# Connecting to a specific endpoint
sub = bt.subtensor( chain_endpoint = "ws://127.0.0.1:9944" )
Methods
nominate
nominate(self, wallet: 'bittensor.Wallet', wait_for_finalization: bool = False, wait_for_inclusion: bool = True) -> bool
Designates the wallet's hotkey as a delegate.
delegate
delegate(self, wallet: 'bittensor.wallet', delegate_ss58: Optional[str] = None, amount: Union[Balance, float] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Adds a specific amount of stake to a delegate using a wallet.
undelegate
undelegate(self, wallet: 'bittensor.wallet', delegate_ss58: Optional[str] = None, amount: Union[Balance, float] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Removes a specific amount of stake from a delegate using a wallet.
set_weights
set_weights(self, wallet: 'bittensor.wallet', netuid: int, uids: Union[torch.LongTensor, list], weights: Union[torch.FloatTensor, list], version_key: int = bittensor.__version_as_int__, wait_for_inclusion:bool = False, wait_for_finalization:bool = False, prompt:bool = False) -> bool
Sets weights for a given netuid.
register
register(self, wallet: 'bittensor.Wallet', netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, prompt: bool = False, max_allowed_attempts: int = 3, output_in_place: bool = True, cuda: bool = False, dev_id: Union[List[int], int] = 0, TPB: int = 256, num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False) -> bool
Registers the wallet to the chain.
burned_register
burned_register(self, wallet: 'bittensor.Wallet', netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, prompt: bool = False) -> bool
Registers the wallet to the chain by recycling TAO.
transfer
transfer(self, wallet: 'bittensor.wallet', dest: str, amount: Union[Balance, float], wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Transfers funds from the wallet to a destination public key address.
get_existential_deposit
get_existential_deposit(self, block: Optional[int] = None) -> Optional[Balance]
Returns the existential deposit for the chain.
serve
serve(self, wallet: 'bittensor.wallet', ip: str, port: int, protocol: int, netuid: int, placeholder1: int = 0, placeholder2: int = 0, wait_for_inclusion: bool = False, wait_for_finalization = True, prompt: bool = False) -> bool
Starts serving on a specific IP, port and protocol for a given netuid.
serve_axon
serve_axon(self, netuid: int, axon: 'bittensor.Axon', use_upnpc: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, prompt: bool
= False) -> bool
Starts serving an Axon for a given netuid.
serve_prometheus
serve_prometheus(self, wallet: 'bittensor.wallet', port: int, netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True) -> bool
Starts serving a Prometheus server on a specific port for a given netuid.
add_stake
add_stake(self, wallet: 'bittensor.wallet', hotkey_ss58: Optional[str] = None, amount: Union[Balance, float] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Adds a specific amount of stake to a hotkey uid.
add_stake_multiple
add_stake_multiple(self, wallet: 'bittensor.wallet', hotkey_ss58s: List[str], amounts: List[Union[Balance, float]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Adds stake to each hotkey in the list from a common coldkey.
unstake
unstake(self, wallet: 'bittensor.wallet', hotkey_ss58: Optional[str] = None, amount: Union[Balance, float] = None, wait_for_inclusion:bool = True, wait_for_finalization:bool = False, prompt: bool = False) -> bool
Removes stake into the wallet coldkey from the specified hotkey uid.
unstake_multiple
unstake_multiple(self, wallet: 'bittensor.wallet', hotkey_ss58s: List[str], amounts: List[Union[Balance, float]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, prompt: bool = False) -> bool
Removes stake from each hotkey in the list to a common coldkey.
Hyperparameters
rho
rho (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network Rho hyperparameter if the network exists. Accepts an integer netuid
representing the unique network ID and an optional integer block
representing the block number.
kappa
kappa (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network Kappa hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
difficulty
difficulty (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network Difficulty hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
burn
burn (self, netuid: int, block: Optional[int] = None ) -> Optional[bittensor.Balance]
Returns the network Burn hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
immunity_period
immunity_period (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network ImmunityPeriod hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_batch_size
validator_batch_size (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network ValidatorBatchSize hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_prune_len
validator_prune_len (self, netuid: int, block: Optional[int] = None ) -> int
Returns the network ValidatorPruneLen hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_logits_divergence
validator_logits_divergence (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network ValidatorLogitsDivergence hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_sequence_length
validator_sequence_length (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network ValidatorSequenceLength hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_epochs_per_reset
validator_epochs_per_reset (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network ValidatorEpochsPerReset hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_epoch_length
validator_epoch_length (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network ValidatorEpochLen hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
validator_exclude_quantile
validator_exclude_quantile (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network ValidatorExcludeQuantile hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
max_allowed_validators
max_allowed_validators(self, netuid: int, block: Optional[int] = None) -> Optional[int]
Returns the network MaxAllowedValidators hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
min_allowed_weights
min_allowed_weights (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network MinAllowedWeights hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
max_weight_limit
max_weight_limit (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network MaxWeightsLimit hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
scaling_law_power
scaling_law_power (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network ScalingLawPower hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
synergy_scaling_law_power
synergy_scaling_law_power (self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the network SynergyScalingLawPower hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
subnetwork_n
subnetwork_n (self, netuid: int, block: Optional[int] = None ) -> int
Returns the network SubnetworkN hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
max_n
max_n (self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the network MaxAllowedUids hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
blocks_since_epoch
blocks_since_epoch (self, netuid: int, block: Optional[int] = None) -> int
Returns the network BlocksSinceLastStep hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
tempo
tempo (self, netuid: int, block: Optional[int] = None) -> int
Returns the network Tempo hyperparameter if the network exists. Inputs are netuid
and an optional block
number.
Account functions
get_total_stake_for_hotkey
get_total_stake_for_hotkey( self, ss58_address: str, block: Optional[int] = None ) -> Optional['bittensor.Balance']
Returns the total stake held on a hotkey including delegated. Inputs are ss58_address
and an optional block
number.
get_total_stake_for_coldkey
get_total_stake_for_coldkey( self, ss58_address: str, block: Optional[int] = None ) -> Optional['bittensor.Balance']
Returns the total stake held on a coldkey across all hotkeys including delegates. Inputs are ss58_address
and an optional block
number.
get_stake_for_coldkey_and_hotkey
get_stake_for_coldkey_and_hotkey( self, hotkey_ss58: str, coldkey_ss58: str, block: Optional[int] = None ) -> Optional['bittensor.Balance']
Returns the stake under a coldkey - hotkey pairing. Inputs are hotkey_ss58
, coldkey_ss58
and an optional block
number.
get_stake
get_stake( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[str,'bittensor.Balance']]
Returns a list of stake tuples (coldkey, balance) for each delegating coldkey including the owner. Inputs are hotkey_ss58
and an optional block
number.
does_hotkey_exist
does_hotkey_exist( self, hotkey_ss58: str, block: Optional[int] = None ) -> bool
Returns true if the hotkey is known by the chain and there are accounts. Inputs are hotkey_ss58
and an optional block
number.
get_hotkey_owner
get_hotkey_owner( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[str]
Returns the coldkey owner of the passed hotkey if it exists. Inputs are hotkey_ss58
and an optional block
number.
get_axon_info
get_axon_info( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[axon_info]
Returns the axon information for the specified hotkey account if it exists. Inputs are hotkey_ss58
and an optional block
number.
get_prometheus_info
get_prometheus_info( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[axon_info]
Returns the prometheus information for the specified hotkey account if it exists. Inputs are hotkey_ss58
and an optional block
number.
Global state
block
@property
def block (self) -> int:
Property that returns the current chain block.
total_issuance
total_issuance (self, block: Optional[int] = None ) -> 'bittensor.Balance'
Returns the total issuance of tokens as of a specified block. If no block is provided, the default is the current block.
total_stake
total_stake (self,block: Optional[int] = None ) -> 'bittensor.Balance'
Returns the total amount of stake as of a specified block. If no block is provided, the default is the current block.
serving_rate_limit
serving_rate_limit (self, block: Optional[int] = None ) -> Optional[int]
Returns the serving rate limit as of a specified block. If no block is provided, the default is the current block.
tx_rate_limit
tx_rate_limit (self, block: Optional[int] = None ) -> Optional[int]
Returns the transaction rate limit as of a specified block. If no block is provided, the default is the current block.
Subnet stake
subnet_exists
subnet_exists( self, netuid: int, block: Optional[int] = None ) -> bool
Checks if a subnet with the given netuid
exists as of a specified block. If no block is provided, the default is the current block.
get_all_subnet_netuids
get_all_subnet_netuids( self, block: Optional[int] = None ) -> List[int]
Returns a list of netuid
s of all subnets as of a specified block. If no block is provided, the default is the current block.
get_total_subnets
get_total_subnets( self, block: Optional[int] = None ) -> int
Returns the total number of subnets as of a specified block. If no block is provided, the default is the current block.
get_subnet_modality
get_subnet_modality( self, netuid: int, block: Optional[int] = None ) -> Optional[int]
Returns the modality of a subnet with a specified netuid
as of a given block. If no block is provided, the default is the current block.
get_subnet_connection_requirement
get_subnet_connection_requirement( self, netuid_0: int, netuid_1: int, block: Optional[int] = None) -> Optional[int]
Returns the connection requirement between two subnets with specified netuid
s as of a given block. If no block is provided, the default is the current block.
get_emission_value_by_subnet
get_emission_value_by_subnet( self, netuid: int, block: Optional[int] = None ) -> Optional[float]
Returns the emission value of a subnet with the given netuid
as of a specified block. If no block is provided, the default is the current block.
get_subnet_connection_requirements
get_subnet_connection_requirements( self, netuid: int, block: Optional[int] = None) -> Dict[str, int]
Returns a dictionary of the connection requirements of a subnet with the given netuid
as of a specified block. If no block is provided, the default is the current block.
get_subnets
get_subnets( self, block: Optional[int] = None ) -> List[int]
Returns a list of all subnets as of a specified block. If no block is provided, the default is the current block.
get_all_subnets_info
get_all_subnets_info( self, block: Optional[int] = None ) -> List[SubnetInfo]
Returns a list of information about all subnets as of a specified block. If no block is provided, the default is the current block.
get_subnet_info
get_subnet_info( self, netuid: int, block: Optional[int] = None ) -> Optional[SubnetInfo]
Returns information about a subnet with a given netuid
as of a specified block. If no block is provided, the default is the current block.
Delegation
is_hotkey_delegate
is_hotkey_delegate( self, hotkey_ss58: str ) -> bool
Checks if a delegate with the specified hotkey exists.
get_delegate_take
get_delegate_take( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[float]
Returns the 'take' (portion of the reward a delegate receives from staking) of a delegate specified by a hotkey as of a given block. If no block is provided, the default is the current block.
get_nominators_for_hotkey
get_nominators_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[str, Balance]]
Returns a list of tuples, each containing a nominator's address and balance for the delegate specified by a hotkey as of a given block. If no block is provided, the default is the current block.
get_delegate_by_hotkey
get_delegate_by_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[DelegateInfo]
Returns information about a delegate specified by a hotkey as of a given block. If no block is provided, the default is the current block.
get_delegates
get_delegates( self, block: Optional[int] = None ) -> List[DelegateInfo]
Returns a list of all delegates as of a specified block. If no block is provided, the default is the current block.
get_delegated
get_delegated( self, coldkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[DelegateInfo, Balance]]
Returns a list of delegates that a given coldkey is staked to, as of a specified block. If no block is provided, the default is the current block. Each item in the list is a tuple containing the delegate's information and the staked balance.
Neuron information per subnet
is_hotkey_registered_any
is_hotkey_registered_any( self, hotkey_ss58: str, block: Optional[int] = None) -> bool
Returns True if the hotkey is registered on any subnet. If no block is specified, the current block is used.
is_hotkey_registered_on_subnet
is_hotkey_registered_on_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None) -> bool
Returns True if the hotkey is registered on a specified subnet. If no block is specified, the current block is used.
is_hotkey_registered
is_hotkey_registered( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None) -> bool
Returns True if the hotkey is registered on a specified subnet. If no block is specified, the current block is used.
get_uid_for_hotkey_on_subnet
get_uid_for_hotkey_on_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None) -> int
Returns the user id (uid) for the hotkey on a specified subnet. If no block is specified, the current block is used.
get_all_uids_for_hotkey
get_all_uids_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None) -> List[int]
Returns a list of all user ids (uids) for the hotkey. If no block is specified, the current block is used.
get_netuids_for_hotkey
get_netuids_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None) -> List[int]
Returns a list of all network user ids (netuids) for the hotkey. If no block is specified, the current block is used.
get_neuron_for_pubkey_and_subnet
get_neuron_for_pubkey_and_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfo]
Returns the neuron information for the hotkey on a specified subnet. If no block is specified, the current block is used.
get_all_neurons_for_pubkey
get_all_neurons_for_pubkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[NeuronInfo]
Returns a list of all neurons for the hotkey. If no block is specified, the current block is used.
neuron_has_validator_permit
neuron_has_validator_permit( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[bool]
Returns True if the neuron with the given uid has a validator permit for the specified subnet. If no block is specified, the current block is used.
neuron_for_wallet
neuron_for_wallet( self, wallet: 'bittensor.Wallet', netuid = int, block: Optional[int] = None ) -> Optional[NeuronInfo]
Returns the neuron information for the given wallet on a specified subnet. If no block is specified, the current block is used.
neuron_for_uid
neuron_for_uid( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfo]
Returns the neuron metadata associated with a given user id (uid) and network user id (netuid) at a specified block, or None if it does not exist.
neurons
neurons(self, netuid: int, block: Optional[int] = None ) -> List[NeuronInfo]
Returns a list of neurons from the chain for a given network user id (netuid) at a specified block.
neuron_for_uid_lite
neuron_for_uid_lite( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfoLite]
Returns the lightweight neuron metadata (without weights and bonds) associated with a given user id (uid) and network user id (netuid) at a specified block, or None if it does not exist.
neurons_lite
neurons_lite(self, netuid: int, block: Optional[int] = None ) -> List[NeuronInfoLite]
Returns a list of lightweight neurons (without weights and bonds) from the chain for a given network user id (netuid) at a specified block.
metagraph
metagraph( self, netuid: int, lite: bool = True ) -> 'bittensor.Metagraph'
Returns the metagraph for the subnet associated with a given network user id (netuid). If 'lite' is True, it returns a metagraph using the lightweight sync (no weights, no bonds).
Legacy
get_balance
get_balance(self, address: str, block: int = None) -> Balance
Returns the token balance for the given Substrate address at a specified block.
get_current_block
get_current_block(self) -> int
Returns the current block number on the chain.
get_balances
get_balances(self, block: int = None) -> Dict[str, Balance]
Returns a dictionary of balances for all addresses at a specified block. The dictionary keys are addresses and values are their corresponding balances.
__str__()
This method is used to provide a string representation of the instance.
str(obj)
If the network
argument equals the chain_endpoint
argument, it returns a string that denotes connecting to a chain endpoint without a known network. Otherwise, it represents connecting to a network with a known endpoint.
__repr__()
This method is used to provide an official string representation of the instance.
repr(obj)
The string returned by this method is identical to the one provided by the __str__()
method.
Initialization
To initialize an instance of the Subtensor
class, you'll need to provide three arguments:
import bittensor as bt
obj = bt.subtensor( config, network, chain_endpoint )
Arguments
config (bt.Config, optional, defaults=bt.subtensor.config())
: Subtensor config object containing arguments from bt.subtensor.config() which are automatically parsed from command line and ENV vars.network (str, optional, default='finney')
: The subtensor network flag. The likely choices are: -- local (local running network) -- finney (main network) -- mock (mock network for testing.) If this option is set it overloads subtensor.chain_endpoint with an entry point node from that network.chain_endpoint (str, default=None)
: The subtensor endpoint flag. If set, overrides the network argument.
Bt.metagraph
Chain state Torch interface
The Metagraph
class holds the chain state of a particular subnetwork at a specific block.
Examples
import bittensor as bt
# Creating metagraph and sync state from a netuid parameter, defaults to connecting to network `finney`
metagraph = bt.metagraph( netuid = 1 )
# Create metagraph and sync with lite = False to sync weights and bonds matrices.
metagraph = bt.metagraph( netuid = 1, lite = False)
# Create metagraph and sync state from local entrypoint, assuming a subtensor chain is currently running.
metagraph = bt.metagraph( netuid = 1, network = 'local' )
# Create an empty metagraph object with no state syncing.
metagraph = bt.metagraph( netuid = 1, sync = False )
# Sync the metagraph at a particular block
metagraph.sync( block = 100000 )
# Save the metagraph to ~/.bittensor/metagraphs/network-$NETWORK_NAME/netuid-#NETUID/block-$BLOCK.pt
metagraph.save()
# Load the latest metagraph by block.
metagraph.load()
Methods
S
def S(self) -> torch.FloatTensor
Returns the total stake.
R
def R(self) -> torch.FloatTensor
Returns the ranks.
I
def I(self) -> torch.FloatTensor
Returns the incentive.
E
def E(self) -> torch.FloatTensor
Returns the emission.
C
def C(self) -> torch.FloatTensor
Returns the consensus.
T
def T(self) -> torch.FloatTensor
Returns the trust.
Tv
def Tv(self) -> torch.FloatTensor
Returns the validator trust.
D
def D(self) -> torch.FloatTensor
Returns the dividends.
B
def B(self) -> torch.FloatTensor
Returns the bonds.
W
def W(self) -> torch.FloatTensor
Returns the weights.
hotkeys
def hotkeys(self) -> List[str]
Returns the list of hotkeys for the axons.
coldkeys
def coldkeys(self) -> List[str]
Returns the list of coldkeys for the axons.
addresses
def addresses(self) -> List[str]
Returns the list of IP addresses for the axons.
str
def __str__(self)
Returns a string representation of the Metagraph.
repr
def __repr__(self)
Returns the same string representation as __str__
.
metadata
def metadata(self) -> dict
Returns a dictionary of Metagraph metadata.
init
def __init__(self, netuid: int, network: str = 'finney', lite: bool = True, sync: bool = True) -> 'metagraph'
Initializes a new instance of the Metagraph.
sync
def sync (self, block: Optional[int] = None, lite: bool = True) -> 'metagraph'
Syncs the Metagraph state at the passed block. Optionally syncs the weights also.
save
def save(self) -> 'metagraph'
Saves the Metagraph object's state_dict under bittensor root directory.
load
def load(self) -> 'metagraph'
Loads the Metagraph object's state_dict from bittensor root directory.
load_from_path
def load_from_path(self, dir_path:str) -> 'metagraph'
Loads the Metagraph object's state_dict from the specified directory path.
Bt.logging
The Logging
interfaces with bittensor internal logging system.
Examples
import bittensor as bt
# Turn on debug logs
bt.debug()
# Turn on trace logs
bt.trace()
# Turn off debug logs
bt.set_debug(False)
# Turn off trace logs
bt.set_trace(False)
# Turn on logging from class definition
bt.logging( set_debug = True )
# Instantiate logging from command line args
bt.logging( bt.logging.config() )
# Turn on logging to file
bt.logging( record_log = True, logging_dir = '/path/to/logs/' )
# Log
bt.logging.info(message)
bt.logging.debug(message)
bt.logging.trace(message)
bt.logging.success(message)
bt.logging.critical(message)
bt.logging.error(message)
>>> 2023-05-29 09:27:25.426 | INFO | message
# Log using prefix suffix design
bt.logging.info(prefix, message)
bt.logging.debug(prefix, message)
bt.logging.trace(prefix, message)
bt.logging.success(prefix, message)
bt.logging.critical(prefix, message)
bt.logging.error(prefix, message)
>>> 2023-05-29 09:27:47.184 | INFO | cat dogs