API Reference¶
Warning
The certlib.log library is currently in the beta stage
of development. This means, in particular, that backward
incompatible changes to the public API are possible (and they
sometimes do happen) in non-major pre-release versions
– until the final 1.0.0 version is released.
General Remarks¶
The certlib.log library is compatible with Python 3.10 and all newer
versions of Python.
Important definition: whenever this document refers to undefined behavior, this should be understood to mean: the API makes no guarantees about what will happen – an exception or a malfunction is likely.
Unless otherwise specified, using the library in a way that contravenes the documented API will result in undefined behavior.
Interface exclusion
The following elements/features are not part of the API (so, in particular, they may change – or be removed if applicable – in minor or patch versions of the library):
-
any elements not documented in this API reference as well as elements that appear only in source code excerpts (available via
<> Source code...drop-down widgets), e.g., specific exception messages; -
specific runtime types of any objects bound to an element of the API (a variable, attribute, parameter or call result), provided that they remain correct with respect to the element’s type annotation, according to the static typing rules;
-
specific behaviors in cases where – according to the documentation – undefined behavior is expected (see the definition above);
-
unofficial support for Python 3.9.
StructuredLogsFormatter
¶
StructuredLogsFormatter(
*,
defaults: Mapping[str, object] | None = None,
auto_makers: Mapping[str, ValueProvider[object] | DottedPath] | None = None,
serializer: OutputSerializer | DottedPath = json.dumps
)
StructuredLogsFormatter(
mapping_of_kwargs_compatible_with_main_signature: (
Mapping[Literal["defaults", "auto_makers", "serializer"], Any]
| KwargsMappingAsLiteralEvaluableString
),
/,
datefmt: None = None,
style: Literal["%"] = "%",
validate: Literal[True] = True,
)
StructuredLogsFormatter(
fmt: None = None,
datefmt: None = None,
style: Literal["%"] = "%",
validate: Literal[True] = True,
*,
defaults: Mapping[str, object] | None = None,
auto_makers: Mapping[str, ValueProvider[object] | DottedPath] | None = None,
serializer: OutputSerializer | DottedPath = json.dumps
)
A subclass of logging.Formatter to form structured log entries.
Tip
If the three call signatures defined by the
StructuredLogsFormatter constructor seem
overwhelming, do not worry. In most cases, you will
only really be interested in the first one (the
main signature). The details are provided below.
See also
For extra information about StructuredLogsFormatter,
including a bunch of usage examples and configuration tips,
see the Tool: StructuredLogsFormatter
section of the User’s Guide.
Constructor arguments (all keyword-only, all optional):
-
defaults(adictor other mapping; default:{}): maps output data keys to values each of which specifies the default value for the respective key (see also themake_base_defaultsmethod…). -
auto_makers(adictor other mapping; default:{}): maps output data keys to respective auto-makers (argumentless factories of output data values – to be automatically called whenever a log entry is prepared). Each auto-maker can be specified either directly or as a string being a dotted path (importable dotted name) that points to an auto-maker (see also themake_base_auto_makersmethod…). -
serializer(a function or other callable; default:json.dumps): a callable that takes one argument being an output datadictand returns a string (presumably, a JSON-serialized form of that dict, even though you may decide to use some other serialization format, if this is OK for you/your organization). Alternatively,serializercan be a dotted path string (importable dotted name) that points to such a callable.
Note
The type of every output data dict (taken by serializer)
is annotated as dict[str, OutputValue] – where, essentially,
the OutputValue element denotes all types of values
that might be returned by the actual implementation of the
prepare_value method. In other words, that method
is what determines those types.
Note that the default implementation of that method always
returns values of json.dumps-compatible types.
Interface restriction
While serializer is allowed to add, remove or replace
top-level items in an output data dict it takes, it should
never mutate any object inside that dict (regardless of the
level of nesting). If some data needs to be modified, completely
new data object(s) should be created – to entirely replace
the respective top-level value in the dict (without mutating
the original object(s) being replaced). Doing otherwise will
result in undefined behavior.
Alternatively, a mapping (especially a dict) of keyword
arguments compatible with the main signature described above, or an
ast.literal_eval-evaluable string representing such a dict,
can be passed to the StructuredLogsFormatter constructor as
the first positional argument.
Moreover, extra arguments that match – by position or
by name – any non-keyword-only parameters defined by the
logging.Formatter base class are accepted but ignored by
the StructuredLogsFormatter constructor, provided that the
value of each (if given) is the default value of the respective
parameter; that is:
-
the first or
fmtargument – needs to beNone(except that it is fine for the first argument to be a mapping or a string representing a mapping, as described above…); -
the second or
datefmtargument – needs to beNone; -
the third or
styleargument – needs to be the"%"string (remember, it will be ignored anyway); -
the fourth or
validateargument – needs to beTrue.
If any of them does not comply, TypeError is raised.
Info
Passing any unexpected (surplus) arguments also causes TypeError.
Note
Thanks to the interface extensions described above, you can
configure a StructuredLogsFormatter even if you are
using the logging.config.fileConfig-specific configuration
format (which, despite its limitations, is still quite popular).
See the formatter_structured section of the fileConfig-style
configuration example
in the User’s Guide.
This class defines the following extendable/overridable hook methods:
get_output_keys_required_in_defaults_or_auto_makersmake_base_defaultsmake_base_auto_makersmake_base_record_attr_to_output_keyformat_timestampget_prepared_output_dataprepare_valueprepare_submapping_keyserialize_prepared_output_data
In some of the individual descriptions of these methods, several other
elements of the StructuredLogsFormatter’s interface and behavior are
also discussed – in particular, the following instance attributes:
Interface restriction
Once an instance of StructuredLogsFormatter is initialized,
the instance attributes listed above should be treated as
read-only and immutable ones (together with all their
contents, regardless of the level of nesting, if any nested data
is present). Doing otherwise will result in undefined behavior.
When it comes to customizing the format of log entry timestamps, the
related attributes defined by the logging.Formatter base class
(namely: converter, default_time_format and default_msec_format)
are ignored by the machinery of StructuredLogsFormatter.
To learn how to actually customize timestamp formatting, please
refer to the description of the StructuredLogsFormatter’s
format_timestamp method.
Additional requirement
Regarding the initialization of a StructuredLogsFormatter
instance, it is also required that every output data key (just
key, as we are not talking about output data values here)
appearing in any of the mappings listed below – be a string
and not exceed 200 characters (otherwise, respectively,
TypeError or ValueError will be raised by the
constructor). The mappings covered by this requirement are:
-
that returned by the
make_base_defaultsmethod, -
the
defaultsargument to the constructor (if actually passed), -
that returned by the
make_base_auto_makersmethod, -
the
auto_makersargument to the constructor (if actually passed), -
that returned by the
make_base_record_attr_to_output_keymethod (note that the requirement in question applies to output data keys – which, when it comes to this mapping, are its values, not its keys; and note that this mapping’s values are also allowed to beNone).
Source code in src/certlib/log.py
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 | |
record_attr_to_output_key
instance-attribute
¶
FORMAT_TIMESTAMP_DEFAULT_KWARGS
class-attribute
¶
Default values of all StructuredLogsFormatter.format_timestamp’s
keyword-only parameters (this mapping may come in handy when you
extend that method in a subclass…).
PREPARE_VALUE_DEFAULT_KWARGS
class-attribute
¶
Default values of all StructuredLogsFormatter.prepare_value’s
keyword-only parameters (this mapping may come in handy when you
extend that method in a subclass…).
unregister_auto_makers
¶
unregister_auto_makers() -> None
A rarely useful method: you may want to invoke it on an instance
of StructuredLogsFormatter only when you need to stop using
that instance but continue using any logging stuff during
further program execution (this does not seem to be a common
case).
Source code in src/certlib/log.py
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 | |
format
¶
Overrides the logging.Formatter’s
implementation with
a StructuredLogsFormatter-specific one.
In some respects, the StructuredLogsFormatter’s implementation
of this method is similar to the logging.Formatter’s original. In
particular, it makes use of the usesTime, formatTime,
formatMessage and formatException
methods in a similar way, and assigns values to the same log record
attributes: message, asctime and exc_text,
making doing so subject to the same conditions (where applicable).
However, it differs from the original in the following ways:
-
of the methods mentioned above, the
formatMessageone is always invoked last (in particular, after the log record’sexc_textattribute is possibly set to a value returned byformatException); -
if the log record’s
exc_infoattribute is a(None, None, None)tuple, then it is treated as if it were falsy (theformatExceptionmethod if not invoked, and the log record’sexc_textattribute is not set); -
the string returned by
formatMessagebecomes the return value of this method (so this method never appends to that string any formatted traceback or formatted stack information, and it does not invokeformatStackeither); that string is supposed to represent the output data dict after serialization (therefore, it should already include, among others, any exception/stack information, if such stuff was requested and obtained); -
regarding how the target value of the log record’s
messageattribute is determined: if themsgattribute of the given log record is an instance ofExtendedMessage(xm), then that instance’sget_message_valuemethod is invoked (directly), instead of the log record’s methodgetMessage;
Source code in src/certlib/log.py
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 | |
usesTime
¶
usesTime() -> bool
Overrides the logging.Formatter’s implementation with one
that always returns True.
Source code in src/certlib/log.py
1356 1357 1358 1359 1360 1361 | |
formatTime
¶
Overrides the logging.Formatter’s
implementation with one that
delegates its entire job to the format_timestamp method
(a StructuredLogsFormatter-specific one), but first checks
if the datefmt argument is None (if it is anything
else, TypeError is raised – which then, typically, is
suppressed and, possibly, printed to sys.stderr by
logging.Handler.handleError…).
Source code in src/certlib/log.py
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 | |
formatMessage
¶
Overrides the logging.Formatter’s implementation with
one that:
-
obtains a ready output data dict by applying the
get_prepared_output_datamethod to the given log record; -
applies the
serialize_prepared_output_datamethod to the obtained output data dict, and returns the result.
Note
The formatMessage name may be slightly misleading. Let
us emphasize that the job of this method is always – also
in the case of the original logging.Formatter class –
to format the crux of the entire log entry, not
just the value of the log record’s message attribute.
Formatting the latter is the job of the log record’s
getMessage method,
or – when the machinery of StructuredLogsFormatter
deals with an ExtendedMessage (xm)
instance – of the get_message_value
method of that instance.
Source code in src/certlib/log.py
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 | |
get_output_keys_required_in_defaults_or_auto_makers
¶
A hook method: extend it in a subclass to impose (more)
output data keys required to be specified for the
purpose of setting defaults and auto_makers.
Note
Obviously, you can also extend/override this method to define fewer required keys than by default (perhaps even no one) if this is OK for you/your organization.
To be more precise: this method’s return value defines the set of keys required to be included in at least one of the following mappings:
-
that returned by the
make_base_defaultsmethod, -
the
defaultsargument to the constructor (if actually passed), -
that returned by the
make_base_auto_makersmethod, -
the
auto_makersargument to the constructor (if actually passed).
Whether this requirement is satisfied is checked during the
formatter initialization. If the check fails, KeyError
is raised.
The default implementation of this method just uses the set of
keys defined as COMMONLY_EXPECTED_NON_STANDARD_OUTPUT_KEYS
(which means that, when invoking the StructuredLogsFormatter
constructor, it is required to specify default values and/or
auto-makers for the keys: "system", "component" and
"component_type").
Info
The requirement in question is considered satisfied also
if some (or all) of the required items are provided only as
defaults (i.e., only by the make_base_defaults’s
result or the defaults constructor argument) and some
(or all) of them define such default values that – after
being transformed by the prepare_value method –
are void values, e.g., None (despite the fact that
such void values are always excluded from the ultimate
collection of default values – see the Related interfaces
note in the make_base_defaults method’s description).
my_formatter = StructuredLogsFormatter(
# This is OK (the requirement is satisfied):
defaults={
"system": None,
"component": None,
"component_type": None,
},
)
In other words, the interface does not prevent you from effectively omitting from output data the keys specified by this method’s result, but you must explicitly state that you really want this (so that it can be safely assumed that this is OK for you/your organization).
Source code in src/certlib/log.py
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 | |
make_base_defaults
¶
A hook method: extend it in a subclass to define basic default values for output.
Automatically invoked on the formatter initialization. Each key in the resultant mapping needs to be an output data key, and each value in that mapping needs to be the desired default value for that key.
The default implementation of this method returns an empty mapping.
Related interfaces
For every instance, the defaults mapping (which is
supposed to specify all default values for any output data
to be generated by the instance) is based on this method’s
result, but is then updated with all items from the defaults
constructor argument (if given), and
adjusted by applying the prepare_value method to each
value, and – then – by deleting each key to which a void
value is assigned (by void value we mean any falsy value
that is not equal to 0, for example: None, "",
[] or {} – but not: False, 0, 0.0, etc.).
Source code in src/certlib/log.py
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 | |
make_base_auto_makers
¶
make_base_auto_makers() -> Mapping[str, ValueProvider[object] | DottedPath]
A hook method: extend it in a subclass to define (more) auto-makers for output.
Automatically invoked on the formatter initialization. Each key in the resultant mapping needs to be an output data key, and each value in that mapping needs to be either an auto-maker or a dotted path (importable dotted name) that points to an auto-maker. Each auto-maker is supposed to be an argumentless function (or a callable object of some other type) returning – whenever it is called – a candidate for an output data value (to be assigned to the respective output data key).
See also
You may want to learn more about auto-makers
themselves by referring to the description of the
register_log_record_attr_auto_maker function
(the machinery of StructuredLogsFormatter
automatically makes use of that function, as appropriate).
It should also be noted, given how the default implementation of the
get_prepared_output_data method works, that every candidate for
an output data value – including those produced by auto-makers
– will be transformed by applying the prepare_value method
to it. Furthermore, whenever the result of this transformation
is a void value (by which we mean any falsy value not equal
to 0, for example: None, "", [] or {} – but
not False, 0, 0.0, etc.), then the respective
key will not be included in the output data dict (even
if a default value is defined for that key; and even if
that key was among those included in the set returned by the
get_output_keys_required_in_defaults_or_auto_makers method
when the formatter instance was initialized).
The default implementation of this method provides a couple of auto-makers which acquire some basic information about the execution environment (e.g., the Python version).
Related interfaces
For every instance, the auto_makers mapping
(which is supposed to specify all auto-makers related
to the instance) is based on this method’s result, but
is then updated with all items from the auto_makers
constructor argument (if given),
and – then – adjusted by prefixing each key with the value
of the auto_made_record_attr_prefix attribute
(which is an automatically generated opaque string, created
separately for each instance of StructuredLogsFormatter,
guaranteed to be unique within a Python interpreter run).
(Therefore – concerning the stuff produced by a particular
formatter instance’s auto-makers – the respective
output data items will be obtained by picking those
log record object’s attributes whose names are prefixed
with the formatter’s auto_made_record_attr_prefix
and using those names with that prefix removed as the
corresponding output data keys. On the other hand, the
formatter will ignore any record attribute names prefixed
with auto_made_record_attr_prefix of any other
formatter instances, as if such attributes did not exist.
Thanks to that, more than one StructuredLogsFormatter
can be used – and they will work independently of each
other, handling just one’s own auto-made stuff.)
Source code in src/certlib/log.py
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | |
make_base_record_attr_to_output_key
¶
A hook method: extend it in a subclass to modify the mapping of log record attribute names to actual output data keys.
Automatically invoked on the formatter initialization. Each key
in the resultant mapping needs to be the name of a (perhaps just
hypothetic) log record attribute, and each value in that mapping
needs to be either the corresponding output data key or None.
In the latter case – given how the default implementation of the
get_prepared_output_data method works – the attribute will
always be omitted whenever output data is generated (note that
this does not apply to log record attributes not included in
the mapping).
The default implementation of this method returns a mapping that
contains all items of STANDARD_RECORD_ATTR_TO_OUTPUT_KEY.
In many cases this will be quite sufficient.
Related interfaces
For every instance, the record_attr_to_output_key
mapping (which is supposed to specify the ultimate mapping of
log record objects’ attribute names to actual keys in output
data) is based on this method’s result, but is then updated
with items suitably derived from auto_makers (to
cause that any log record attribute name prefixed with the
instance’s auto_made_record_attr_prefix is mapped
to an output data key being just the unprefixed version of
that name; see the Related interfaces note in the description
of the make_base_auto_makers method).
Source code in src/certlib/log.py
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 | |
format_timestamp
¶
format_timestamp(
record: logging.LogRecord,
*,
timezone: dt.tzinfo | None = dt.timezone.utc,
timestamp_as_datetime: Callable[
[float, dt.tzinfo | None], dt.datetime
] = dt.datetime.fromtimestamp,
utc_offset_to_custom_suffix: Mapping[
dt.timedelta | None, str
] = types.MappingProxyType(
{dt.timedelta(0): "Z", None: " <UNCERTAIN TIMEZONE>"}
)
) -> str
A hook method: extend/override it in a subclass to modify/redefine
how, for each log entry, the formatted timestamp (asctime) is
determined.
This method is invoked by the formatTime method, with a log
record (typically, an instance of logging.LogRecord) as the
sole argument. The log record is expected to have its created
attribute
already set to a float number representing a Unix timestamp.
What should be returned by this method is a string (presumably,
derived somehow from the aforementioned created attribute of
the log record) that will later be assigned (by the format
method) to the log record’s asctime attribute.
The default implementation of this method should be sufficient
in most cases. It converts the value of the given log record’s
created attribute to a string being an ISO-8601-compliant
date and time representation, with microsecond resolution.
If no optional keyword arguments are given (which is how
this method is invoked by formatTime), the resultant time
representation is a UTC one (with Z, rather than +00:00,
as its suffix), e.g.: "2026-03-15 13:48:56.726403Z".
Note
The logging.Formatter-specific attributes related to
timestamp formatting (converter, default_time_format and
default_msec_format) are ignored.
Tip
When extending this method in a subclass, you may want to make
your custom implementation invoke the default one with some
keyword arguments specified. In such a case, you may want to
reach for their default values defined by the signature of
StructuredLogsFormatter.format_timestamp; if so, refer
to the StructuredLogsFormatter.FORMAT_TIMESTAMP_DEFAULT_KWARGS
mapping. For example:
import datetime as dt
import types
from certlib.log import StructuredLogsFormatter
class EstTimezoneOrientedStructuredLogsFormatter(StructuredLogsFormatter):
UTC_OFFSET_FOR_EST = dt.timedelta(hours=(-5))
DEFAULT_TIMEZONE = dt.timezone(UTC_OFFSET_FOR_EST)
DEFAULT_UTC_OFFSET_TO_CUSTOM_SUFFIX = types.MappingProxyType({
# Let's use the base class's stuff in a *DRY* manner...
**StructuredLogsFormatter.FORMAT_TIMESTAMP_DEFAULT_KWARGS[
'utc_offset_to_custom_suffix'
],
# ...and extend it with this-class-specific stuff:
**{
# If the suffix were to be `-05:00`,
# we want it to be ` EST` instead.
UTC_OFFSET_FOR_EST: ' EST',
},
})
def format_timestamp(
self,
record,
*,
timezone=DEFAULT_TIMEZONE,
utc_offset_to_custom_suffix=DEFAULT_UTC_OFFSET_TO_CUSTOM_SUFFIX,
**kwargs,
):
return super().format_timestamp(
record,
timezone=timezone,
utc_offset_to_custom_suffix=utc_offset_to_custom_suffix,
**kwargs,
)
Source code in src/certlib/log.py
1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 | |
get_prepared_output_data
¶
get_prepared_output_data(record: logging.LogRecord) -> dict[str, OutputValue]
A hook method: extend/override it in a subclass to modify/redefine
how an output data dict is obtained from a log record object
(which, at least typically, is a logging.LogRecord instance).
Subclass behavior restriction
This method should not mutate the given log record or any data it carries (regardless of the level of nesting, if any nested data is present). If some data needs to be modified, a completely new data object(s) should be created.
Moreover, each output data dict returned by this method
should be a newly created dict (never the original
log record’s __dict__ itself), so that during later stages
of processing it will be safe to add, remove or replace any
top-level items in that dict.
Doing otherwise will result in undefined behavior.
The default implementation of this method should be sufficient
in most cases. To build a new output data dict, it digs into
the given log record (and if that log record’s msg attribute is
an ExtendedMessage instance – also into that instance…).
While doing that, it also looks at the formatter attributes:
record_attr_to_output_key (when determining output data
keys; see also: make_base_record_attr_to_output_key) and
defaults (to suitably complement the extracted output
data with default items; see also: make_base_defaults),
as well as makes intensive use of the prepare_value method
(to ensure that each value in the resultant output data dict
will be prepared for serialization). To make this description
comprehensive, several details – regarding the resultant output
data dict’s top-level keys and values – need to be
clarified:
-
when those keys and values are being determined based on the log record’s contents, any log record attributes that have been created by auto-makers belonging to some other instances of
StructuredLogsFormatter(i.e., not belonging toself) are excluded from consideration, meaning no output data items are created from them (for certain low-level details, see the Related interfaces note in the description of themake_base_auto_makersmethod…); -
all existing log record attributes whose names are mapped in
record_attr_to_output_keyto some output data keys – are being included in the output data dict; this applies, in particular, to any log record attributes that have been created by auto-makers belonging to this (self) instance ofStructuredLogsFormatter(see the Related interfaces note in the description of themake_base_record_attr_to_output_keymethod…); -
all existing log record attributes whose names are mapped in
record_attr_to_output_keytoNone– are being excluded; -
all existing log record attributes not created by any auto-maker and not included in
record_attr_to_output_key– are being included (!) in the output data dict, using each record attribute name as the corresponding output data key (as if it was mapped inrecord_attr_to_output_keyto itself); -
only keys that are instances of
strare ever included (meaning that any non-string keys, even if they appeared at some stage of processing, are always excluded), and every key is truncated to a maximum length of 200 characters (if it was longer); compare this with the treatment of nested keys (see the description of theprepare_submapping_keymethod…); -
when it comes to transforming every value by applying the aforementioned
prepare_valuemethod to it, if the result of this transformation turns out to be a void value (by which we mean any falsy value not equal to0, for example:None,"",[]or{}– but not:False,0,0.0, etc.), then the respective key is excluded (even if it should be included according to any other rule described above; and even if some default value is defined for that key!); note that nested values, even if void, are never subject to such an exclusion (at least if the default implementations ofprepare_valueandprepare_submapping_keyare used); -
potential item collisions (which might occur, for example, when some key is present both in the
ExtendedMessage’sdatamapping and among other data obtained from the log record’s content, and the value to be assigned to that key varies depending on which of those two sources of information is checked) – are avoided by suffixing problematic keys with one or more underscore character(s), as needed to prevent key duplication; such cases are expected to be rare.
Note
The said key truncation occurs before the said key deduplication – so it is possible, although very rare in practice, that appending underscore(s) to certain keys (as described above) will result in some keys ending up a little longer than 200 characters.
Source code in src/certlib/log.py
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 | |
prepare_value
¶
prepare_value(
value: object,
*,
to_str_types: tuple[type, ...] = (
dt.date,
dt.datetime,
dt.time,
decimal.Decimal,
enum.Enum,
fractions.Fraction,
ipaddress.IPv4Address,
ipaddress.IPv4Interface,
ipaddress.IPv4Network,
ipaddress.IPv6Address,
ipaddress.IPv6Interface,
ipaddress.IPv6Network,
uuid.UUID,
),
pass_thru_types: tuple[type, ...] = (str, int, float, bool, type(None)),
exclude_from_seq_types: tuple[type, ...] = (str, bytes, bytearray),
is_dataclass: Callable[[object], bool] = dataclasses.is_dataclass,
dataclass_as_dict: Callable[[Any], dict[str, Any]] = dataclasses.asdict,
last_resort: Callable[[object], str] = repr,
**kwargs: Any
) -> OutputValue
A hook method: extend/override it in a subclass to modify/redefine how every value in an output data dict is prepared before the actual data serialization.
Subclass behavior restriction
This method should not mutate its argument or anything inside it (regardless of the level of nesting, if any nested data is present). If some data needs to be modified, a completely new value should be created (to be used as the prepared value to be returned), without mutating any existing objects. Doing otherwise will result in undefined behavior.
The default implementation of this method should be sufficient
in most cases. It converts any value (even such one that is
deeply nested inside sequences/mappings – thanks to recursive
calls, always passing all keyword arguments from the parent
call…) to a form that can be serialized with json.dumps
(and which is – hopefully – short yet still readable, especially
regarding instances of such types as: exceptions,
dataclasses, typical named tuples,
enum.Enum, uuid.UUID as well as the essential types
from the datetime and ipaddress modules). When it
comes to preparing any keys contained in a value which is
a mapping (e.g., a dict) – see the
prepare_submapping_key method…
Tip
When extending this method in a subclass, you may want to make
your custom implementation invoke the default one with some
keyword arguments specified. In such a case, you may want to
reach for their default values defined by the signature of
StructuredLogsFormatter.prepare_value; if so, refer to
the StructuredLogsFormatter.PREPARE_VALUE_DEFAULT_KWARGS
mapping. For example:
import array, pprint
import attrs # <- 3rd party package used just in this example
from certlib.log import StructuredLogsFormatter
_BASE_KWARGS = StructuredLogsFormatter.PREPARE_VALUE_DEFAULT_KWARGS
class MyEnhancedStructuredLogsFormatter(StructuredLogsFormatter):
@staticmethod
def default_is_dataclass(obj):
base_is_dataclass = _BASE_KWARGS['is_dataclass']
return base_is_dataclass(obj) or attrs.has(type(obj))
@staticmethod
def default_dataclass_as_dict(obj):
base_is_dataclass = _BASE_KWARGS['is_dataclass']
base_dataclass_as_dict = _BASE_KWARGS['dataclass_as_dict']
return (base_dataclass_as_dict(obj) if base_is_dataclass(obj)
else attrs.asdict(obj))
def prepare_value(
self,
value,
*,
exclude_from_seq_types = (
*_BASE_KWARGS['exclude_from_seq_types'],
memoryview,
array.array,
),
is_dataclass=default_is_dataclass,
dataclass_as_dict=default_dataclass_as_dict,
last_resort=pprint.pformat,
**kwargs,
):
return super().prepare_value(
value,
exclude_from_seq_types=exclude_from_seq_types,
is_dataclass=is_dataclass,
dataclass_as_dict=dataclass_as_dict,
last_resort=last_resort,
**kwargs,
)
Source code in src/certlib/log.py
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 | |
prepare_submapping_key
¶
A hook method: extend/override it in a subclass to modify/redefine
how to prepare, before the actual data serialization, every key
in every mapping (e.g., in a dict) being a value inside an
output data dict (possibly deeply nested within it).
The default implementation of this method should be sufficient in
most cases. It applies str to the given key (converting it
to a string if it was not one already) and truncates the result to
a maximum length of 200 characters (if longer).
Note
prepare_value is what invokes this method – so (let
us stress that!) this method is not applied to top-level
keys in the output data dict, but is applied to each key
in every mapping that prepare_value takes as an input
value (also, in every dict created by prepare_value
as a result of converting an exception, named tuple or
dataclass instance…). All of this is true for the default
implementation of prepare_value. It is recommended
(yet not enforced) that any custom implementations of the
prepare_value method make use of this method in a
similar way.
Source code in src/certlib/log.py
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 | |
serialize_prepared_output_data
¶
serialize_prepared_output_data(output_data: dict[str, OutputValue]) -> str
A hook method: extend/override it in a subclass to modify/redefine the output data serialization procedure.
Subclass behavior restriction
While it is OK to add, remove or replace top-level items in the given output data dict, this method should never mutate any object inside it (regardless of the level of nesting). If some data needs to be modified, completely new data object(s) should be created – to entirely replace the respective top-level value in the dict (without mutating the original object(s) being replaced). Doing otherwise will result in undefined behavior.
The default implementation of this method should be sufficient in
most cases. It just applies the serializer callable to the
given output data dict, and returns the result.
Related interfaces
By default, the serializer attribute is set to the
standard json.dumps function, but this can be changed
by specifying the serializer argument when invoking the
StructuredLogsFormatter constructor.
Source code in src/certlib/log.py
2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 | |
xm: Final = ExtendedMessage
module-attribute
¶
xm is a convenience alias of ExtendedMessage.
ExtendedMessage
¶
ExtendedMessage(
pattern: str = "",
/,
*args: object | ValueProvider[object],
exc_info: Any = None,
stack_info: bool = False,
stacklevel: int = 1,
**data: object | ValueProvider[object],
)
ExtendedMessage(
data: Mapping[str, object | ValueProvider[object]],
/,
*,
exc_info: Any = None,
stack_info: bool = False,
stacklevel: int = 1,
)
ExtendedMessage(
pattern: object,
/,
*args: object | ValueProvider[object],
exc_info: Any = None,
stack_info: bool = False,
stacklevel: int = 1,
**data: object | ValueProvider[object],
)
A tool thanks to which you can:
-
conveniently emit structured log entries, especially if
StructuredLogsFormatteris in use; -
use the modern
{}-based style of log message formatting, or – if you just need to log pure data – simply omit passing the text message pattern (regardless of what formatter is in use); -
defer the creation of some values (if it is costly) until the log entry is actually about to be emitted (regardless of what formatter is in use).
There is a convenience alias of this class: xm. As being
very short, it is simply much more ergonomic than the actual class
name – given that this tool is intended to be used every time you
log something…
Usage examples:
import datetime as dt, hashlib, ipaddress, logging, sys
from certlib.log import xm
logging.info(xm("Hello {}!", sys.platform))
logging.info(xm("Maxsize is {maxsize:x}", maxsize=sys.maxsize))
logging.warning(xm(
connection_count=42,
client_ip=ipaddress.IPv4Address("192.168.0.121"),
local_time=dt.datetime.now(),
# Value creation deferred until log entry is about to be emitted:
payload_hash=lambda: hashlib.sha256(b'...payload...').hexdigest(),
))
some_data_dict = globals()
logging.debug(xm(some_data_dict))
See also
For extra information about ExtendedMessage,
including a bunch of usage examples, see the Tool:
xm section of the
User’s Guide.
Constructor arguments (all optional):
-
first positional argument (default:
""): the text message pattern. Expected to be a string, or any truthy object that could be converted to a string by applyingstrto it. The pattern may contain{}-formatting-style replacement fields (perhaps with a'!'-separated conversion marker, and/or a':'-separated format spec). The given object is assigned to thepatternattribute intact, unless it is falsy – then it is ignored, and just""(empty string) is assigned to that attribute. -
extra positional arguments (if any): positional args to format the text message (they need to match positional/numbered replacement fields in the text message pattern – see the first positional argument described above). A
tupleof these arguments is assigned to theargsattribute. -
exc_info(keyword-only; default:None): its usage and related behavior are nearly identical to those of the same-named argument tologging.Logger’s methods (see the relevant fragment of the documentation for theloggingmodule). This argument is assigned to theexc_infoattribute. -
stack_info(keyword-only; default:False): its usage and related behavior are nearly identical to those of the same-named argument tologging.Logger’s methods (see the relevant fragment of the documentation for theloggingmodule). This argument is assigned to thestack_infoattribute. -
stacklevel(keyword-only; default:1): its usage and related behavior are nearly identical to those of the same-named argument tologging.Logger’s methods (see the relevant fragment of the documentation for theloggingmodule). This argument is assigned to thestacklevelattribute. -
extra keyword arguments (if any): all of them become extra data items – to be included in the output data dict if
StructuredLogsFormatteris used, or to be appended to the text message (in a form resembling the keyword arguments syntax) if some other formatter is in use. Adictof those extra data items is always stored as thedataattribute. Moreover, any items whose names match some named replacement fields in the text message pattern (specified as the first positional argument) will take part in formatting the actual text message (regardless of what formatter is in use).
Alternatively, a mapping (e.g., a dict) of extra data
items can be passed to the constructor as the
first positional argument. The effect is the same as if each of
its items was passed as an extra keyword argument (without passing
any positional arguments). The mapping, after conversion to a dict,
is assigned to the data attribute.
Interface restriction
If a mapping is passed as the first positional argument,
then passing any other arguments except exc_info,
stack_info and stacklevel causes TypeError.
When it comes to the arguments exc_info, stack_info
and stacklevel, they should not be included in that
mapping (doing so will result in undefined behavior). Each of
them, if to be specified, should only be specified as a real
keyword argument.
Interface restriction
A string.templatelib.Template object (which is typically
created by evaluating a t-string)
should not be passed as the first positional argument
(doing so will result in undefined behavior). This possibility
is reserved for future versions of certlib.log, in which
dedicated support for such objects may be provided.
Interface restriction
If you pass a stack_info and/or stacklevel argument
to the ExtendedMessage (xm) constructor, you
should not pass stack_info or stacklevel to the
related logger method call
(doing so will result in undefined behavior).
# All WRONG (!!!):
logger.info(xm('Foo', stack_info=True), stack_info=True)
logger.info(xm('Foo', stack_info=True), stacklevel=2)
logger.info(xm('Foo', stacklevel=2), stack_info=True)
logger.info(xm('Foo', stacklevel=2), stacklevel=2)
logger.info(xm('Foo', stack_info=True), stack_info=True, stacklevel=2)
logger.info(xm('Foo', stack_info=True, stacklevel=2), stack_info=True)
logger.info(xm('Foo', stack_info=True, stacklevel=2), stack_info=True, stacklevel=2)
# (and similar...)
# OK:
logger.info(xm('Foo', stack_info=True))
logger.info(xm('Foo', stacklevel=2))
logger.info(xm('Foo', stack_info=True, stacklevel=2))
# Also OK:
logger.info(xm('Foo'), stack_info=True)
logger.info(xm('Foo'), stacklevel=2)
logger.info(xm('Foo'), stack_info=True, stacklevel=2)
Whenever a formatter (of any type) processes a log record whose msg
attribute (which typically is just what has been passed to the logger
method call as the first positional argument) is an ExtendedMessage
(xm) instance, that instance’s method get_message_value is
invoked: either directly – by the StructuredLogsFormatter’s
machinery; or indirectly, via __str__ – by the standard
machinery that other formatter types use.
Interface restriction
When you pass an instance of ExtendedMessage
as the first positional argument to a logger method
call,
you should not pass to that call any other positional
arguments (doing so will result in undefined behavior).
# WRONG (!!!):
logger.info(xm('{}, {} and {}'), 'Athos', 'Porthos', 'Aramis')
# OK:
logger.info(xm('{}, {} and {}', 'Athos', 'Porthos', 'Aramis'))
Note
If a text message pattern (not a mapping) is passed to the
ExtendedMessage (xm) constructor as the
first positional argument and no extra positional
or keyword arguments are provided (except exc_info,
stack_info and stacklevel) – that is, if both
of the attributes args and data of the
resultant ExtendedMessage instance are empty – then
the get_message_value method will not attempt to
format the text message with str.format; instead, it
will treat the pattern as an already formatted text message.
logger.info(xm('answer: {}', 42)) # message will be 'answer: 42'
logger.info(xm('answer: {}')) # message will be 'answer: {}' [sic!]
This mimics how the standard logging machinery handles
a log record whose args attribute is empty (when only a
text message pattern is specified, without any values to be
interpolated).
If any extra positional or keyword arguments to the constructor
– except exc_info, stack_info and stacklevel –
or any values included in the extra data mapping (if passed to the
constructor as the first positional argument) are function or
method objects (precisely: instances of any types included in
ExtendedMessage.recognized_callable_arg_or_data_item_types),
then – as part of processing the ExtendedMessage instance by a
formatter – each of those functions/methods will be called to
obtain the actual value, which will then replace (respectively,
in args or data) the called function/method.
To be precise: all those calls-and-replacements will be
triggered when any of the following methods is invoked on the
ExtendedMessage instance for the first time: get_message_value,
get_record_msg_and_args_equivalent_info, __str__ or
iter_str_parts (with the proviso that the last one returns an
iterator
which, to achieve the effect in question, needs to be iterated over,
at least partially). Each of those calls-and-replacements will
be made at most once per instance of ExtendedMessage (see
the _ensure_callable_args_and_data_items_resolved method’s
description…).
Thanks to that mechanism, if the creation of some value is expected
to be costly, you can wrap it in a function/method (in particular,
in an argumentless lambda) to defer that costly operation until
the value becomes necessary (which may never happen if, for example,
the specified log level is lower than the configured threshold).
Such a function/method is expected to take no arguments (therefore,
if it is a method, it should already be bound to some instance or
class).
Multithreading-related restriction
None of those functions/methods should acquire any locks
that might also be acquired by any code making use of some
logging stuff (because, in particular, that could result in
a deadlock).
Source code in src/certlib/log.py
2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 | |
recognized_callable_arg_or_data_item_types
class-attribute
¶
recognized_callable_arg_or_data_item_types: tuple[
type[ValueProvider[object]], ...
]
For ExtendedMessage, this tuple contains the runtime types of
function and bound method objects – both in the user-defined
and built-in variants (precisely: types.FunctionType,
types.BuiltinFunctionType, types.MethodType and
types.MethodWrapperType). You can override this attribute in
your subclass to redefine the runtime types of values in args
and data that shall be called to obtain the actual values
(see the part of the ExtendedMessage constructor’s description
containing a reference to this attribute…).
get_message_value
¶
get_message_value() -> str
Automatically invoked by the StructuredLogsFormatter’s
machinery to obtain a string to be assigned to the log record’s
message attribute.
Interface restriction
Once this method is invoked on an ExtendedMessage instance,
any attempts (regarding that instance) to replace/mutate any of
the objects assigned to the pattern, args and
data attributes or anything inside them (regardless of
the level of nesting, if any nested data is present) – are no
loger allowed. Doing so will result in undefined behavior.
The default implementation of this method should be sufficient
in most cases. It converts pattern to a string, and then
– only if args and/or data contain any items –
invokes that string’s format method, passing to
it all items of args as positional arguments and all items
of data as keyword arguments. A string being the result of
the above operation(s) is cached (for any further invocations
of this method on the same instance) and returned.
Subclass behavior requirement
This method should always invoke the
_ensure_callable_args_and_data_items_resolved
method before starting the actual work (the default
implementation already does that). Failing to do so
will result in undefined behavior.
Note
Apart from the aforementioned use by the machinery
of StructuredLogsFormatter, this method is also
invoked by the ExtendedMessage’s implementation
of __str__ (which is important for formatters
that are not instances of StructuredLogsFormatter).
Source code in src/certlib/log.py
2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 | |
get_record_msg_and_args_equivalent_info
¶
get_record_msg_and_args_equivalent_info(
*, pattern_result_key: str | None, args_result_key: str | None
) -> Mapping[str, object]
Automatically invoked by the StructuredLogsFormatter’s
machinery to get a value to be included in the output data
dict under the key corresponding to the log record’s msg
attribute. The returned value is supposed to be a mapping
that conveys relevant information from the ExtendedMessage
instance – to the extent that corresponds to the information
typically conveyed by the msg and args attributes of log
records when ExtendedMessage is not used.
Interface restriction
Once this method is invoked on an ExtendedMessage instance,
any attempts (regarding that instance) to replace/mutate any of
the objects assigned to the args and data
attributes or anything inside them (regardless of the level
of nesting, if any nested data is present) – are no loger
allowed. Doing so will result in undefined behavior.
The default implementation should be sufficient in most cases. It
returns a mapping containing zero, one or two items. Specifically
– each of the following if the key is not None and the
value is not falsy:
-
the given
pattern_result_key– mapped to the value of thepatternattribute, -
the given
args_result_key– mapped to the value of theargsattribute.
Subclass behavior requirement
This method should always invoke the
_ensure_callable_args_and_data_items_resolved
method before starting the actual work (the default
implementation already does that). Failing to do so
will result in undefined behavior.
Source code in src/certlib/log.py
2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 | |
__str__
¶
__str__() -> str
Invoked when str is applied to an ExtendedMessage instance.
This is done, in particular, by the machinery related to typical
non-StructuredLogsFormatter formatters (specifically, by the
log record method getMessage)
– to obtain a string to be assigned to the message
attribute
of the log record.
Interface restriction
Once this method is invoked on an ExtendedMessage instance,
any attempts (regarding that instance) to replace/mutate any of
the objects assigned to the pattern, args and
data attributes or anything inside them (regardless of
the level of nesting, if any nested data is present) – are no
loger allowed. Doing so will result in undefined behavior.
The default implementation of this method should be sufficient
in most cases. It invokes the iter_str_parts method
(which, in particular, invokes get_message_value…)
and concatenates any yielded strings (if more than one) using
" | " as the separator.
Subclass behavior requirement
This method should always invoke the
_ensure_callable_args_and_data_items_resolved
method before starting the actual work (the default
implementation already does that). Failing to do so
will result in undefined behavior.
Source code in src/certlib/log.py
3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 | |
__repr__
¶
__repr__() -> str
Invoked when repr is applied to an ExtendedMessage
instance (typically, for debug purposes).
The default implementation of this method should be sufficient
in most cases. It invokes the iter_argument_reprs method,
concatenates any yielded strings (if more than one) using ", "
as the separator, adds the parentheses, and prefixes the whole
thing with the class name.
Source code in src/certlib/log.py
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 | |
iter_str_parts
¶
Invoked by the __str__ method.
Interface restriction
Once this method is invoked on an ExtendedMessage instance,
any attempts (regarding that instance) to replace/mutate any of
the objects assigned to the pattern, args and
data attributes or anything inside them (regardless of
the level of nesting, if any nested data is present) – are no
loger allowed. Doing so will result in undefined behavior.
The default implementation of this method yields zero, one or two strings. Specifically – each of the following if not empty:
-
the result of an invocation of the
get_message_valuemethod, -
a representation of the
datamapping’s items (formatted in a way that resembles the syntax for specifying keyword arguments, but without the parentheses).
Subclass behavior requirement
This method should always invoke the
_ensure_callable_args_and_data_items_resolved
method before starting the actual work (the default
implementation already does that). Failing to do so
will result in undefined behavior.
Source code in src/certlib/log.py
3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 | |
iter_argument_reprs
¶
Invoked by the __repr__ method.
The default implementation of this method yields string
representations of the arguments to the ExtendedMessage
(xm) constructor which would be needed to create an
instance equivalent to this one (self).
Source code in src/certlib/log.py
3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 | |
_ensure_callable_args_and_data_items_resolved
¶
_ensure_callable_args_and_data_items_resolved() -> None
Interface exclusion
This method is not part of the API – except that
it is allowed to be invoked by any methods implemented by
possible subclasses of ExtendedMessage.
This method processes the items of args and data –
by calling each encountered instance of any type included in
ExtendedMessage.recognized_callable_arg_or_data_item_types,
and then replacing that value with the result of that call.
Each of those calls is made without arguments.
This method can be safely invoked multiple times on the same
instance, even in the case of concurrent invocations. The
implementation guarantees that none of the calls in question
will be made more than once per instance of ExtendedMessage.
Interface restriction
Once this method is invoked on an ExtendedMessage
instance, any other attempts (regarding this instance)
to replace/mutate any of the collections assigned to the
args and data attributes or anything
inside them (regardless of the level of nesting, if any
nested data is present) – are no loger allowed. Doing
so will result in undefined behavior.
Source code in src/certlib/log.py
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 | |
make_constant_value_provider
¶
make_constant_value_provider(value: T) -> ValueProvider[T]
A trivial (yet sometimes useful) helper: given an arbitrary object
(value), create an argumentless function that will always
return that object (note that such argumentless functions can be
used as auto-makers).
Source code in src/certlib/log.py
3337 3338 3339 3340 3341 3342 3343 3344 | |
register_log_record_attr_auto_maker
¶
register_log_record_attr_auto_maker(
rec_attr: str, auto_maker: ValueProvider[object]
) -> None
For the specified log record attribute name (rec_attr),
register the given auto-maker callable (auto_maker).
By calling this function you ensure that, from now on, the specified attribute will be automatically set on every new log record object – to a value returned by the specified auto-maker.
The auto-maker needs to be an argumentless function or
any other object that can be called with no arguments (see:
ValueProvider). A call to it will be made at most once for
each newly created log record (only if the logger is enabled for
the respective log level), in the thread in which the current logger
method call is being executed (shortly after the log record is
created by a logger,
yet before any handlers,
filters and
formatters
process that record). Obviously, the returned values are allowed to
vary depending on the context (or even with each call).
If, for the specified attribute name, some auto-maker is already
registered, this function raises KeyError.
Tip
The get method of
a ContextVar may be a
good candidate for an auto-maker.
Note
Typically, you do not need to use the
register_log_record_attr_auto_maker function directly,
because the machinery of the StructuredLogsFormatter class
does this for you – at instantiation time (see the descriptions of the
StructuredLogsFormatter constructor’s
auto_makers argument and the StructuredLogsFormatter’s
make_base_auto_makers
method).
That machinery will also take care of avoiding record attribute name collisions.
Warning
If you use this function directly, you need to take care of
avoiding record attribute name collisions by yourself. When
the internal auto-makers machinery attempts to assign an
auto-maker-produced value to the respective attribute of
a log record but the log record already has that attribute set,
then KeyError is raised (which will typically bubble up
to the caller of the currently executed logger method). This
behavior mimics how the machinery of the standard logging
module reacts to collisions between extra items and existing
attributes of a log record.
Source code in src/certlib/log.py
3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 | |
unregister_log_record_attr_auto_maker
¶
unregister_log_record_attr_auto_maker(rec_attr: str) -> None
For the given log record attribute name (rec_attr), unregister
the previously registered auto-maker.
If, for the specified attribute name, no auto-maker is currently
registered, this function raises KeyError.
Source code in src/certlib/log.py
3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 | |
COMMONLY_EXPECTED_NON_STANDARD_OUTPUT_KEYS
module-attribute
¶
COMMONLY_EXPECTED_NON_STANDARD_OUTPUT_KEYS: Final[Set[str]] = frozenset(
{"system", "component", "component_type"}
)
STANDARD_RECORD_ATTR_TO_OUTPUT_KEY
module-attribute
¶
STANDARD_RECORD_ATTR_TO_OUTPUT_KEY: Final[Mapping[str, str | None]] = (
types.MappingProxyType(
{
"asctime": "timestamp",
"exc_info": "exc_info",
"exc_text": "exc_text",
"funcName": "func",
"levelname": "level",
"levelno": "levelno",
"lineno": "lineno",
"message": "message",
"msg": "message_base",
"name": "logger",
"pathname": "src",
"process": "pid",
"processName": "process_name",
"stack_info": "stack_info",
"thread": "thread_id",
"threadName": "thread_name",
"taskName": "async_task_name",
"args": None,
"created": None,
"filename": None,
"module": None,
"msecs": None,
"relativeCreated": None,
}
)
)
Static Typing Helpers¶
Note
In your day-to-day work with the certlib.log library, you do not
need to delve into this stuff.
Interface exclusion
The flavor of any type aliases – i.e., whether they are
TypeAlias-annotated ones or type
statement-made
ones – is not part of the API.
ValueProvider
¶
__call__() -> Value
A protocol which describes any callable object (e.g., a function) that takes no arguments and returns some value (returned values may vary with each call). It is worth noting that, in particular, every auto-maker is supposed to be such a callable object.
Typing details
-
In the above
__call__()signature, theValueelement is a type variable. -
That variable has no upper bound (or, in other words, its upper bound is
object). -
The
ValueProviderprotocol is generic. It is covariant in that variable.
OutputSerializer
¶
__call__(output_data: dict[str, OutputValue], /) -> str
A protocol which describes a callable object
(e.g., a function) that takes an output data dict (supposedly,
returned by StructuredLogsFormatter.get_prepared_output_data)
as the sole positional argument, and returns a string representing
that dict in serialized form (typically, but not necessarily, in
JSON format).
Interface restriction
While it is OK to add, remove or replace top-level items in an
output data dict, a callable used as an OutputSerializer
should never mutate any object inside such a dict (regardless
of the level of nesting). If some data needs to be modified,
completely new data object(s) should be created – to entirely
replace the respective top-level value in the output data
dict (without mutating the original object(s) being replaced).
Typing details
In the above __call__() signature, as everywhere else, the
OutputValue element is just an alias for Any
(see below).
OutputValue
module-attribute
¶
OutputValue = Any
A type alias which is used to annotate top-level values in output data dicts.
Required preparation–serialization compatibility
Typically, an output data dict (annotated as dict[str,
OutputValue]) is:
-
created by
StructuredLogsFormatter.get_prepared_output_data– with each value obtained by invokingStructuredLogsFormatter.prepare_value(whose return type annotation isOutputValue); -
passed to
StructuredLogsFormatter.serialize_prepared_output_data– and then passed by it toStructuredLogsFormatter.serializer(which is annotated asOutputSerializer).
So every serializer is
required to be capable of serializing any dict that maps strings
to values whose types, generally referred to as OutputValue,
are decided by the (default or customized) implementation of
prepare_value
(and/or by customized implementations of
get_prepared_output_data and/or
serialize_prepared_output_data,
if they change something in this regard).
Note
The json.dumps function, which is the default
serializer,
satisfies this requirement for the default implementations
of prepare_value,
get_prepared_output_data and
serialize_prepared_output_data.
Typing details
Accurately expressing the above requirement using static
types would be difficult (at least without making things
overly complicated), so that approach is not taken. Instead,
OutputValue is defined just as an alias for the
Any special type.
DottedPath
module-attribute
¶
DottedPath = str
A type alias which is used to annotate strings being a dotted path (importable dotted name).
KwargsMappingAsLiteralEvaluableString
module-attribute
¶
KwargsMappingAsLiteralEvaluableString = str
A type alias
which is used to annotate strings being an ast.literal_eval-evaluable
representation of a mapping (dict) of keyword arguments that are compatible
with the main (first) signature of the StructuredLogsFormatter
constructor.