Skip to main content

A library for dealing with JSON as python objects

Project description

jsonobject

Build Status Downloads Supported Versions Contributors

jsonobject is a python library for handling deeply nested JSON objects as well-schema'd python objects.

jsonobject is made by Dimagi, where we build, use, and contribute to OSS in our mission to reduce inequity in the world.

jsonobject is inspired by and largely API compatible with the Document/DocumentSchema portion of couchdbkit. Because jsonobject is not only simpler and standalone, but also faster, we also maintain a fork of couchdbkit, jsonobject-couchdbkit, that is backed by jsonobject and works seamlessly as a swap-in replacement for the main library.

It is used heavily in CommCare HQ (source), and the API is largely stable, but more advanced features may change in the future.

Getting Started

To install using pip, simply run

pip install jsonobject

Example

The code below defines a simple user model, and its natural mapping to JSON.

from jsonobject import *

class User(JsonObject):
    username = StringProperty()
    name = StringProperty()
    active = BooleanProperty(default=False)
    date_joined = DateTimeProperty()
    tags = ListProperty(unicode)

Once it is defined, it can be used to wrap or produce deserialized JSON.

>>> user1 = User(
    name='John Doe',
    username='jdoe',
    date_joined=datetime.datetime.utcnow(),
    tags=['generic', 'anonymous']
)
>>> user1.to_json()
{
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'date_joined': '2013-08-05T02:46:58Z',
    'tags': [u'generic', u'anonymous']
}

Notice that the datetime is converted to an ISO format string in JSON, but is a real datetime on the object:

>>> user1.date_joined
datetime.datetime(2013, 8, 5, 2, 46, 58)

The jsonobject Constructor

A JsonObject subclass that has been defined as User above comes with a lot of built-in functionality. The basic operations are

  1. Make a new object from deserialized JSON (e.g. the output of json.loads)
  2. Construct a new object with given values
  3. Modify an object
  4. Dump to deserialized json (e.g. the input of json.dumps)

1 & 2 are accomplished with the constructor. There are two main ways to call the constructor:

User(
    name='John Doe',
    username='jdoe',
    date_joined=datetime.datetime.utcnow(),
    tags=['generic', 'anonymous']
)

as above (satisfies #2) and

User({
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'date_joined': '2013-08-05T02:46:58Z',
    'tags': [u'generic', u'anonymous']
})

(satisfies #1). These two styles can also be mixed and matched:

User({
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'tags': [u'generic', u'anonymous']
}, date_joined=datetime.datetime.utcnow())

Notice how datetimes are stored as strings in the deserialized JSON, but as datetime.datetimes in the nice python object—we will refer to these as the "json" representation and the "python" representation, or alternatively the "unwrapped" representation and the "wrapped" representation.

Gotcha. When calling the constructor, remember that the keyword argument style requires you to pass in the "python" representation (e.g. a datetime) while the json-wrapping style of passing in a dict requires you to give it in the "json" representation (e.g. a datetime-formatted string).

Property Types

There are two main kinds of property types: scalar types (like string, bool, int, datetime, etc.) and container types (list, dict, set). They are dealt with separately below.

Scalar Types

All scalar properties can take the value None in addition to the values particular to their type (strings, bools, etc). If set to the wrong type, properties raise a jsonobject.exceptions.BadValueError:

class Foo(jsonobject.JsonObject):
    b = jsonobject.BooleanProperty()
>>> Foo(b=0)
Traceback (most recent call last):
  [...]
jsonobject.exceptions.BadValueError: 0 not of type <type 'bool'>

jsonobject.StringProperty

Maps to a unicode. Usage:

class Foo(jsonobject.JsonObject):
    s = jsonobject.StringProperty()

If you set it to an ascii str it will implicitly convert to unicode:

>>> Foo(s='hi')  # converts to unicode
Foo(s=u'hi')

If you set it to a non-ascii str, it will fail with a UnicodeDecodeError:

>>> Foo(s='\xff')
Traceback (most recent call last):
  [...]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)

jsonobject.BooleanProperty

Maps to a bool.

jsonobject.IntegerProperty

Maps to an int or long.

jsonobject.FloatProperty

Maps to a float.

jsonobject.DecimalProperty

Maps to a decimal.Decimal and stored as a JSON string. This type, unlike FloatProperty, stores the "human" representation of the digits. Usage:

class Foo(jsonobject.JsonObject):
    number = jsonobject.DecimalProperty()

If you set it to an int or float, it will implicitly convert to Decimal:

>>> Foo(number=1)
Foo(number=Decimal('1'))
>>> Foo(number=1.2)
Foo(number=Decimal('1.2'))

If you set it to a str or unicode, however, it raises an AssertionError:

>>> Foo(number='1.0')
Traceback (most recent call last):
  [...]
AssertionError

Todo: this should really raise a BadValueError.

If you pass in json in which the Decimal value is a str or unicode, but it is malformed, it throws the same errors as decimal.Decimal.

>>> Foo({'number': '1.0'})
Foo(number=Decimal('1.0'))
>>> Foo({'number': '1.0.0'})
Traceback (most recent call last):
  [...]
decimal.InvalidOperation: Invalid literal for Decimal: '1.0.0'

jsonobject.DateProperty

Maps to a datetime.date and stored as a JSON string of the format '%Y-%m-%d'. Usage:

class Foo(jsonobject.JsonObject):
    date = jsonobject.DateProperty()

Wrapping a badly formatted string raises a BadValueError:

>>> Foo({'date': 'foo'})
Traceback (most recent call last):
  [...]
jsonobject.exceptions.BadValueError: 'foo' is not a date-formatted string

jsonobject.DateTimeProperty

Maps to a timezone-unaware datetime.datetime and stored as a JSON string of the format '%Y-%m-%dT%H:%M:%SZ'.

While it works perfectly with good inputs, it is extremely sloppy when it comes to dealing with inputs that don't match the exact specified format. Rather than matching stricty, it simply truncates the string to the first 19 characters and tries to parse that as '%Y-%m-%dT%H:%M:%S'. This ignores both microseconds and, even worse, the timezone. This is a holdover from couchdbkit.

In newer versions of jsonboject, you may optionally specify a DateTimeProperty as exact:

class Foo(jsonobject.JsonObject):
    date = jsonobject.DateTimeProperty(exact=True)

This provides a much cleaner conversion model that has the following properties:

  1. It preserves microseconds
  2. The incoming JSON representation must match '%Y-%m-%dT%H:%M:%S.%fZ' exactly. (This is similar to the default output, except for the mandatory 6 decimal places, i.e. milliseconds.)
  3. Representations that don't match exactly will be rejected with a BadValueError.

Recommendation: If you are not locked into couchdbkit's earlier bad behavior, you should always use the exact=True flag on DateTimePropertys and TimePropertys (below).

jsonobject.TimeProperty

Maps to a datetime.time, stored as a JSON string of the format '%H:%M:%S'.

To get access to milliseconds and strict behavior, use the exact=True setting which strictly accepts the format '%H:%M:%S.%f'. This is always recommended. For more information please read the previous section on DateTimeProperty.

Container Types

Container types generally take a first argument, item_type, specifying the type of the contained objects.

jsonobject.ObjectProperty(item_type)

Maps to a dict that has a schema specified by item_type, which must be itself a subclass of JsonObject. Usage:

class Bar(jsonobject.JsonObject):
    name = jsonobject.StringProperty()


class Foo(jsonobject.JsonObject):
    bar = jsonobject.ObjectProperty(Bar)

If not specified, it will be set to a new object with default values:

>>> Foo()
Foo(bar=Bar(name=None))

If you want it set to None you must do so explicitly.

jsonobject.ListProperty(item_type)

Maps to a list with items of type item_type, which can be any of the following:

  • an instance of a property class. This is the most flexible option, and all validation (required, etc.) will be done as as specified by the property instance.
  • a property class, which will be instantiated with required=True
  • one of their corresponding python types (i.e. int for IntegerProperty, etc.)
  • a JsonObject subclass

Note that a property class (as well as the related python type syntax) will be instantiated with required=True, so ListProperty(IntegerProperty) and ListProperty(int) do not allow None, and ListProperty(IntegerProperty()) does allow None.

The serialization behavior of whatever item type is given is recursively applied to each member of the list.

If not specified, it will be set to an empty list.

jsonobject.SetProperty(item_type)

Maps to a set and stored as a list (with only unique elements). Otherwise its behavior is very much like ListProperty's.

jsonobject.DictProperty(item_type)

Maps to a dict with string keys and values specified by item_type. Otherwise its behavior is very much like ListProperty's.

If not specified, it will be set to an empty dict.

Other

jsonobject.DefaultProperty()

This flexibly wraps any valid JSON, including all scalar and container types, dynamically detecting the value's type and treating it with the corresponding property.

Property options

Certain parameters may be passed in to any property.

For example, required is one such parameter in the example below:

class User(JsonObject):
    username = StringProperty(required=True)

Here is a complete list of properties:

  • default

    Specifies a default value for the property

  • name

    The name of the property within the JSON representation*. This defaults to the name of the python property, but you can override it if you wish. This can be useful, for example, to get around conflicting with python keywords:

    >>> class Route(JsonObject):
    ...     from_ = StringProperty(name='from')
    ...     to = StringProperty()  # name='to' by default
    >>> Route(from_='me', to='you').to_json()
    {'from': u'me', 'to': u'you'}
    

    Notice how an underscore is present in the python property name ('from_'), but absent in the JSON property name ('from').

    \*If you're wondering how `StringProperty`'s `name` parameter could possibly default to `to` in the example above, when it doesn't have access to the `Route` class's properties at init time, you're completely right. The behavior described is implemented in `JsonObject`'s `__metaclass__`, which *does* have access to the `Route` class's properties.
  • choices

    A list of allowed values for the property. (Unless otherwise specified, None is also an allowed value.)

  • required

    Defaults to False. For scalar properties requires means that the value None may not be used. For container properties it means they may not be empty or take the value None.

  • exclude_if_none

    Defaults to False. When set to true, this property will be excluded from the JSON output when its value is falsey. (Note that currently this is at odds with the parameter's name, since the condition is that it is falsey, not that it is None).

  • validators

    A single validator function or list of validator functions. Each validator function should raise an exception on invalid input and do nothing otherwise.

  • verbose_name

    This property does nothing and was added to match couchdbkit's API.

Performance Comparison with Couchdbkit

In order to do a direct comparison with couchdbkit, the test suite includes a large sample schema originally written with couchdbkit. It is easy to swap in jsonobject for couchdbkit and run the tests with each. Here are the results:

$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 1.403s

OK
$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 0.153s

OK

Development Lifecycle

jsonobject versions follow semantic versioning. Version information can be found in CHANGES.md.

Information for developers and maintainers, such as how to run tests and release new versions, can be found in LIFECYCLE.md.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonobject-2.3.0.dev20250225184101.tar.gz (570.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

jsonobject-2.3.0.dev20250225184101-cp313-cp313-musllinux_1_2_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ x86-64

jsonobject-2.3.0.dev20250225184101-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

jsonobject-2.3.0.dev20250225184101-cp312-cp312-musllinux_1_2_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

jsonobject-2.3.0.dev20250225184101-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

jsonobject-2.3.0.dev20250225184101-cp311-cp311-musllinux_1_2_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

jsonobject-2.3.0.dev20250225184101-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

jsonobject-2.3.0.dev20250225184101-cp310-cp310-musllinux_1_2_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

jsonobject-2.3.0.dev20250225184101-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

jsonobject-2.3.0.dev20250225184101-cp39-cp39-musllinux_1_2_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ x86-64

jsonobject-2.3.0.dev20250225184101-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

File details

Details for the file jsonobject-2.3.0.dev20250225184101.tar.gz.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101.tar.gz
Algorithm Hash digest
SHA256 4c5596c9c124868bbeed13d401bd8cef75e21bb1467b09d8bd1b19e30780c652
MD5 815a1e856752b32ebfbe5b08b1a74071
BLAKE2b-256 1d7fb90ab726acae35a8c2cb48d97fa24dd53608b8ac61666ddde9a1542713b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101.tar.gz:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 1add43582170f7b4d8a9bd8165392f26f06e56cf21b891f92d3210d60377d8a4
MD5 5d6bf7b0142d9a55873d32a5bd4ed80c
BLAKE2b-256 c71821d2c844177ad3b232c9f3480830ac568a77361133965e5513fe526a1be0

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp313-cp313-musllinux_1_2_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c20787cae997a59fd8f065ea241461cb2171d1fb8ed1e7a38ba35795a57c5476
MD5 e2523f3e7bd674321f70620d93990d94
BLAKE2b-256 4c5ee0aecede3f545d7358aed589ac81983a04d5739dee03eb822a17a4cd34d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 dcdfafafe9854cdf33f3479d99c3f05c2cb1cde46decd79e201b91ac6c134af8
MD5 2506e71af363ee51dc522b11ff6c1c05
BLAKE2b-256 58f9ecaed1bfbfe391200f90e6d692a498648c0b9bc5669ed6ce2e1e25e6e1eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp312-cp312-musllinux_1_2_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 12dc4a675efb27a8edbc88fb326683a531821c9d8ca1591dd6573ba797c9c04a
MD5 e2252e83d2318cfb0712edcee073d2c1
BLAKE2b-256 c0b65f90a8ba6113f6fd30ba5135fa2dfad2c7e39079c8e03fdd4a3201186b92

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 09345f4f9626cfcad9283a91d0f8c1d7ce2a48b3a9d1c2a016ae5d042a1f516a
MD5 adc0c0e753b361e66e76478f586d6e64
BLAKE2b-256 4ee912b3bd7f28255fd76e46743e09b062c9bd4585b4e888ab1bd7ef4e355a45

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp311-cp311-musllinux_1_2_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c0e609c8a7c3dafa866f7061f1c66a7a8fbc90df711006bf58e75fc10ccec10b
MD5 5b6bdba68390eb851b74c053bf6c72b6
BLAKE2b-256 95e02857bf36b2ba0318158d90c2f7a34bdaf5878788a39d72b1bfb2d3a88b94

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp310-cp310-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 2353148a0d7b7a8431b0dd43e18c84fe974ea2354c15980daac5796059efa59e
MD5 75ffb1fa101c7a3da01fc09b10a75204
BLAKE2b-256 9a59724354716db00aa11e80b9b8a561b075eb3aeca64377b9714d8c31d3e2ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp310-cp310-musllinux_1_2_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1098a90a1ee3b1082418bce9e760dda6b3e56b408152a1c2ddbefbdade5b831f
MD5 369257ef3081b0d6ef963f16b7a908d4
BLAKE2b-256 77df1d7233d939f423fd9fda38dcd6bc13e67f0685a6b9bf42d3569406e93da7

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp39-cp39-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 569f6d37b937f7d164882b477cddc1ce2527f15677f1879490d95d11e2af6a81
MD5 e971590f8245e4dab492d130b8cdba43
BLAKE2b-256 1aff3bd8800a4252003119e82df93ff7235e0f00f8bb65801fc92cc615b77912

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp39-cp39-musllinux_1_2_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonobject-2.3.0.dev20250225184101-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for jsonobject-2.3.0.dev20250225184101-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 768870fd45227195ba664311655af980fa0217763358ad7696164916de6be40f
MD5 a9b4430183939427c8df1607fe8b2777
BLAKE2b-256 1814708dd688cf3a6c35940fbe4d3157f85b412c2f14012baba86e5610b8d8ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonobject-2.3.0.dev20250225184101-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on dimagi/jsonobject

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page