Skip to main content

Execution helpers for simplified usage of subprocess and ssh.

Project description

exec-helpers

https://travis-ci.org/python-useful-helpers/exec-helpers.svg?branch=master https://dev.azure.com/python-useful-helpers/exec-helpers/_apis/build/status/python-useful-helpers.exec-helpers?branchName=master https://coveralls.io/repos/github/python-useful-helpers/exec-helpers/badge.svg?branch=master Documentation Status https://img.shields.io/pypi/v/exec-helpers.svg https://img.shields.io/pypi/pyversions/exec-helpers.svg https://img.shields.io/pypi/status/exec-helpers.svg https://img.shields.io/github/license/python-useful-helpers/exec-helpers.svg https://img.shields.io/badge/code%20style-black-000000.svg

Execution helpers for simplified usage of subprocess and ssh. Why another subprocess wrapper and why no clear paramiko?

Historically paramiko offers good ssh client, but with specific limitations: you can call command with timeout, but without receiving return code, or call command and wait for return code, but without timeout processing.

In the most cases, we are need just simple SSH client with comfortable API for calls, calls via SSH proxy and checking return code/stderr. This library offers this functionality with connection memorizing, deadlock free polling and friendly result objects (with inline decoding of YAML, JSON, binary or just strings). In addition this library offers the same API for subprocess calls, but with specific limitation: no parallel calls (for protection from race conditions).

Pros:

Python 3.5
Python 3.6
Python 3.7
PyPy3 3.5+

This package includes:

  • SSHClient - historically the first one helper, which used for SSH connections and requires memorization due to impossibility of connection close prediction. Several API calls for sFTP also presents.

  • SSHAuth - class for credentials storage. SSHClient does not store credentials as-is, but uses SSHAuth for it. Objects of this class can be copied between ssh connection objects, also it used for execute_through_host.

  • Subprocess - subprocess.Popen wrapper with timeouts, polling and almost the same API, as SSHClient (except specific flags, like cwd for subprocess and get_tty for ssh).

  • async_api.Subprocess - the same, as Subprocess helper, but works with asyncio. .. note:: for Windows ProactorEventLoop or another non-standard event loop should be used!

  • ExecResult - class for execution results storage. Contains exit code, stdout, stderr and getters for decoding as JSON, YAML, string, bytearray and brief strings (up to 7 lines).

  • ExitCodes - enumerator for standard Linux exit codes. BASH return codes (broduced from signal codes) also available.

Usage

SSHClient

Basic initialization of SSHClient can be done without construction of specific objects:

client = exec_helpers.SSHClient(host, username="username", password="password")

If ssh agent is running - keys will be collected by paramiko automatically, but if keys are in specific location - it should be loaded manually and provided as iterable object of paramiko.RSAKey.

For advanced cases or re-use of credentials, SSHAuth object should be used. It can be collected from connection object via property auth.

Creation from scratch:

auth = exec_helpers.SSHAuth(
    username='username',  # type: typing.Optional[str]
    password='password',  # type: typing.Optional[str]
    key=None,  # type: typing.Optional[paramiko.RSAKey]
    keys=None,  # type: typing.Optional[typing.Iterable[paramiko.RSAKey]],
    key_filename=None,  # type: typing.Union[typing.List[str], str, None]
    passphrase=None,  # type: typing.Optional[str]
)

Key is a main connection key (always tried first) and keys are alternate keys. Key filename is afilename or list of filenames with keys, which should be loaded. Passphrase is an alternate password for keys, if it differs from main password. If main key now correct for username - alternate keys tried, if correct key found - it became main. If no working key - password is used and None is set as main key.

Context manager is available, connection is closed and lock is released on exit from context.

Subprocess

Context manager is available, subprocess is killed and lock is released on exit from context.

Base methods

Main methods are execute, check_call and check_stderr for simple executing, executing and checking return code and executing, checking return code and checking for empty stderr output. This methods are almost the same for SSHCleint and Subprocess, except specific flags.

result = helper.execute(
    command,  # type: str
    verbose=False,  # type: bool
    timeout=1 * 60 * 60,  # type: typing.Union[int, float, None]
    **kwargs
)
result = helper.check_call(
    command,  # type: str
    verbose=False,  # type: bool
    timeout=1 * 60 * 60,  # type: type: typing.Union[int, float, None]
    error_info=None,  # type: typing.Optional[str]
    expected=(0,),  # type: typing.Iterable[typing.Union[int, ExitCodes]]
    raise_on_err=True,  # type: bool
    # Keyword only:
    exception_class=CalledProcessError,  # typing.Type[CalledProcessError]
    **kwargs
)
result = helper.check_stderr(
    command,  # type: str
    verbose=False,  # type: bool
    timeout=1 * 60 * 60,  # type: type: typing.Union[int, float, None]
    error_info=None,  # type: typing.Optional[str]
    raise_on_err=True,  # type: bool
    # Keyword only:
    expected=(0,),  # typing.Iterable[typing.Union[int, ExitCodes]]
    exception_class=CalledProcessError,  # typing.Type[CalledProcessError]
)
result = helper(  # Lazy way: instances are callable and uses `execute`.
    command,  # type: str
    verbose=False,  # type: bool
    timeout=1 * 60 * 60,  # type: typing.Union[int, float, None]
    **kwargs
)

If no STDOUT or STDERR required, it is possible to disable this FIFO pipes via **kwargs with flags open_stdout=False and open_stderr=False.

The next command level uses lower level and kwargs are forwarded, so expected exit codes are forwarded from check_stderr. Implementation specific flags are always set via kwargs.

If required to mask part of command from logging, log_mask_re attribute can be set global over instance or providden with command. All regex matched groups will be replaced by ‘<*masked*>’.

result = helper.execute(
    command="AUTH='top_secret_key'; run command",  # type: str
    verbose=False,  # type: bool
    timeout=1 * 60 * 60,  # type: typing.Optional[int]
    log_mask_re=r"AUTH\s*=\s*'(\w+)'"  # type: typing.Optional[str]
)

result.cmd will be equal to AUTH=’<*masked*>’; run command

ExecResult

Execution result object has a set of useful properties:

  • cmd - Command

  • exit_code - Command return code. If possible to decode using enumerators for Linux -> it used.

  • stdin -> str. Text representation of stdin.

  • stdout -> typing.Tuple[bytes]. Raw stdout output.

  • stderr -> typing.Tuple[bytes]. Raw stderr output.

  • stdout_bin -> bytearray. Binary stdout output.

  • stderr_bin -> bytearray. Binary stderr output.

  • stdout_str -> str. Text representation of output.

  • stderr_str -> str. Text representation of output.

  • stdout_brief -> str. Up to 7 lines from stdout (3 first and 3 last if >7 lines).

  • stderr_brief -> str. Up to 7 lines from stderr (3 first and 3 last if >7 lines).

  • stdout_json - STDOUT decoded as JSON.

  • stdout_yaml - STDOUT decoded as YAML.

  • timestamp -> typing.Optional(datetime.datetime). Timestamp for received exit code.

SSHClient specific

SSHClient commands support get_pty flag, which enables PTY open on remote side. PTY width and height can be set via keyword arguments, dimensions in pixels are always 0x0.

Possible to call commands in parallel on multiple hosts if it’s not produce huge output:

results = SSHClient.execute_together(
    remotes,  # type: typing.Iterable[SSHClient]
    command,  # type: str
    timeout=1 * 60 * 60,  # type: type: typing.Union[int, float, None]
    expected=None,  # type: typing.Optional[typing.Iterable[int]]
    raise_on_err=True,  # type: bool
    # Keyword only:
    exception_class=ParallelCallProcessError  # typing.Type[ParallelCallProcessError]
)
results  # type: typing.Dict[typing.Tuple[str, int], exec_result.ExecResult]

Results is a dict with keys = (hostname, port) and and results in values. By default execute_together raises exception if unexpected return code on any remote.

For execute through SSH host can be used execute_through_host method:

result = client.execute_through_host(
    hostname,  # type: str
    command,  # type: str
    auth=None,  # type: typing.Optional[SSHAuth]
    target_port=22,  # type: int
    timeout=1 * 60 * 60,  # type: type: typing.Union[int, float, None]
    verbose=False,  # type: bool
    # Keyword only:
    get_pty=False,  # type: bool
    width=80,  # type: int
    height=24  # type: int
)

Where hostname is a target hostname, auth is an alternate credentials for target host.

SSH client implements fast sudo support via context manager: Commands will be run with sudo enforced independently from client settings for normal usage:

with client.sudo(enforce=True):
    ...

Commands will be run without sudo independently from client settings for normal usage:

with client.sudo(enforce=False):
    ...

“Permanent client setting”:

client.sudo_mode = mode  # where mode is True or False

SSH Client supports sFTP for working with remote files:

with client.open(path, mode='r') as f:
    ...

For fast remote paths checks available methods:

  • exists(path) -> bool

>>> conn.exists('/etc/passwd')
True
  • stat(path) -> paramiko.sftp_attr.SFTPAttributes

>>> conn.stat('/etc/passwd')
<SFTPAttributes: [ size=1882 uid=0 gid=0 mode=0o100644 atime=1521618061 mtime=1449733241 ]>
>>> str(conn.stat('/etc/passwd'))
'-rw-r--r--   1 0        0            1882 10 Dec 2015  ?'
  • isfile(path) -> bool

>>> conn.isfile('/etc/passwd')
True
  • isdir(path) -> bool

>>> conn.isdir('/etc/passwd')
False

Additional (non-standard) helpers:

  • mkdir(path: str) - execute mkdir -p path

  • rm_rf(path: str) - execute rm -rf path

  • upload(source: str, target: str) - upload file or from source to target using sFTP.

  • download(destination: str, target: str) - download file from target to destination using sFTP.

Subprocess specific

Keyword arguments:

  • cwd - working directory.

  • env - environment variables dict.

async_api.Subprocess specific

All standard methods are coroutines. Async context manager also available.

Example:

async with helper:
  result = await helper.execute(
      command,  # type: str
      verbose=False,  # type: bool
      timeout=1 * 60 * 60,  # type: typing.Union[int, float, None]
      **kwargs
  )

Testing

The main test mechanism for the package exec-helpers is using tox. Available environments can be collected via tox -l

CI systems

For code checking several CI systems is used in parallel:

  1. Travis CI: is used for checking: PEP8, pylint, bandit, installation possibility and unit tests. Also it’s publishes coverage on coveralls.

  2. Azure Pipelines: is used for windows compatibility checking.

  3. coveralls: is used for coverage display.

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

exec-helpers-3.4.0.post1.tar.gz (74.3 kB view details)

Uploaded Source

Built Distributions

exec_helpers-3.4.0.post1-py3-none-any.whl (47.1 kB view details)

Uploaded Python 3

exec_helpers-3.4.0.post1-cp37-cp37m-win_amd64.whl (736.7 kB view details)

Uploaded CPython 3.7m Windows x86-64

exec_helpers-3.4.0.post1-cp37-cp37m-win32.whl (637.5 kB view details)

Uploaded CPython 3.7m Windows x86

exec_helpers-3.4.0.post1-cp36-cp36m-win_amd64.whl (736.4 kB view details)

Uploaded CPython 3.6m Windows x86-64

exec_helpers-3.4.0.post1-cp36-cp36m-win32.whl (637.5 kB view details)

Uploaded CPython 3.6m Windows x86

exec_helpers-3.4.0.post1-cp35-cp35m-win_amd64.whl (682.8 kB view details)

Uploaded CPython 3.5m Windows x86-64

exec_helpers-3.4.0.post1-cp35-cp35m-win32.whl (586.8 kB view details)

Uploaded CPython 3.5m Windows x86

File details

Details for the file exec-helpers-3.4.0.post1.tar.gz.

File metadata

  • Download URL: exec-helpers-3.4.0.post1.tar.gz
  • Upload date:
  • Size: 74.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 PyPy/5.10.1

File hashes

Hashes for exec-helpers-3.4.0.post1.tar.gz
Algorithm Hash digest
SHA256 14dcad34bdcdb9472197e988e3930cc5001497a088f09b5d1d65b0ee480506ea
MD5 298b06af85a2047bb21b7a524c234365
BLAKE2b-256 02fe7f2a22acbdab1cc8f38134bd054960db6d66e4c37a4fd778c05ced263b2c

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-py3-none-any.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-py3-none-any.whl
  • Upload date:
  • Size: 47.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 PyPy/5.10.1

File hashes

Hashes for exec_helpers-3.4.0.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 f000773e732d00dd8532dee0dbb38f757aa69f64f8d6ed3c619f69173a3d862d
MD5 b31531ef705a79af003d146fa2f25b7d
BLAKE2b-256 9e4896c871809aad0f8248f590ab4b225c1b3220225ee78341cc4e9cb9685a72

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 736.7 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.0

File hashes

Hashes for exec_helpers-3.4.0.post1-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 3075d2050655046d25b3fced20ecf0a3af2f3747b751fef7ef35da89cfe319f2
MD5 c6ea4f8e4b034bf9ae51bd4c08a22fc6
BLAKE2b-256 af1cddb6a9ad4c180b46e7ca7fc763d0f0aaba61523d3abd0f95f544892beb9b

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp37-cp37m-win32.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp37-cp37m-win32.whl
  • Upload date:
  • Size: 637.5 kB
  • Tags: CPython 3.7m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.0

File hashes

Hashes for exec_helpers-3.4.0.post1-cp37-cp37m-win32.whl
Algorithm Hash digest
SHA256 5229d9dc3ad4da3ba11066c9cb75bc4d66c3c5b665de7e410540a177454affde
MD5 eb69d5ce0a2f63a0a48897572ca8a2ea
BLAKE2b-256 13162a061bc71e12a9bd76eb218fc9d48e1d6984602e1a85924541ff4acd82ed

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp37-cp37m-manylinux1_x86_64.whl.

File metadata

File hashes

Hashes for exec_helpers-3.4.0.post1-cp37-cp37m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 f95c052196fc679b4b23c95aa245af854e2a57ffdf05abb487886329ce5712bb
MD5 3cbcd366393bb6b7b9294bfd94b3f18b
BLAKE2b-256 050805dbc6abd5cea2129222db22460d4e745778d60551254a3056bcb1ca7f2a

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp37-cp37m-manylinux1_i686.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp37-cp37m-manylinux1_i686.whl
  • Upload date:
  • Size: 2.8 MB
  • Tags: CPython 3.7m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 PyPy/5.10.1

File hashes

Hashes for exec_helpers-3.4.0.post1-cp37-cp37m-manylinux1_i686.whl
Algorithm Hash digest
SHA256 9a681cf2155418a2ad748f7223457ed37685bbc27adca5bbf38a9f4294e2a59a
MD5 421316fac8b544effe5f77a17dd70a38
BLAKE2b-256 f10b4026f4963f338bb5d0aaf5a95ed245e50fbc5f86de5d4b383d3d48cf5fca

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp36-cp36m-win_amd64.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp36-cp36m-win_amd64.whl
  • Upload date:
  • Size: 736.4 kB
  • Tags: CPython 3.6m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.4

File hashes

Hashes for exec_helpers-3.4.0.post1-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 c8b685226e218cd4f5ebd11a9792892f2c5b5fee3a1837f29f55c26867d03d77
MD5 927982e7219bca2ccb775df3e051f5b6
BLAKE2b-256 480ff3e2c05d7bc09a9d748b27a44ecfbabfe7f37e0262c83a0348cc578c7265

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp36-cp36m-win32.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp36-cp36m-win32.whl
  • Upload date:
  • Size: 637.5 kB
  • Tags: CPython 3.6m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.4

File hashes

Hashes for exec_helpers-3.4.0.post1-cp36-cp36m-win32.whl
Algorithm Hash digest
SHA256 061ec6ee69e2564f426de7af544a76a51a477d6c0557481141dee9f309d5af02
MD5 c010fa10ce18fac3c38af591e9bf3f36
BLAKE2b-256 8cc472d8c62dd28221a747326f24847106341c50fb23e212a59f709c123af048

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp36-cp36m-manylinux1_x86_64.whl.

File metadata

File hashes

Hashes for exec_helpers-3.4.0.post1-cp36-cp36m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 a051f26edcd83e9e3a8508260d9b150d3504a9ad21f964e54046f68ccfdc7e85
MD5 0234eb8dd4783d1354f7a6f818f32de4
BLAKE2b-256 f482f8e108178d803b4427c7d055d711178f1569d5e0098de14c9129880794ca

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp36-cp36m-manylinux1_i686.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp36-cp36m-manylinux1_i686.whl
  • Upload date:
  • Size: 2.8 MB
  • Tags: CPython 3.6m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 PyPy/5.10.1

File hashes

Hashes for exec_helpers-3.4.0.post1-cp36-cp36m-manylinux1_i686.whl
Algorithm Hash digest
SHA256 7226f4d5ad60c08d4484d10d16bb87d746ad51add82c43cce2a53cef8f5103fb
MD5 519c4425b6d9f134db37fc1667c52bc1
BLAKE2b-256 b3ca70a40265bd00f8a87531bbc0ccb65990d0754c8f1a629d34e4e82ee1f0c0

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp35-cp35m-win_amd64.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp35-cp35m-win_amd64.whl
  • Upload date:
  • Size: 682.8 kB
  • Tags: CPython 3.5m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.5.4

File hashes

Hashes for exec_helpers-3.4.0.post1-cp35-cp35m-win_amd64.whl
Algorithm Hash digest
SHA256 86892ec7cb9273a1b093df0345443b98347476adc1d852e6c9ad7a4f102fe8db
MD5 ad3757d7c9998198228f282d631812ba
BLAKE2b-256 352fa242ef7b4b91a3b08991028bc000bcd6631cdb157582120369722fd19c79

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp35-cp35m-win32.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp35-cp35m-win32.whl
  • Upload date:
  • Size: 586.8 kB
  • Tags: CPython 3.5m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.5.4

File hashes

Hashes for exec_helpers-3.4.0.post1-cp35-cp35m-win32.whl
Algorithm Hash digest
SHA256 d2c4c957309449b474e12df986b0011ec37a7407ddb258913f3608976c943bad
MD5 a4fa2b1e9df78773e992cec393fbea6f
BLAKE2b-256 4fcc0805ba238f6d7ba86f5e8aebc6bdf230d1a891f5c44c6e94fda24f16ace8

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp35-cp35m-manylinux1_x86_64.whl.

File metadata

File hashes

Hashes for exec_helpers-3.4.0.post1-cp35-cp35m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 2fb3def64c8352558ccfb7ca653489e82649a65a5b30c3c894bfc3e1d06a2c0c
MD5 545f01e6ac33ef6309d71e1e06b9649e
BLAKE2b-256 03dec34e577e0053ea169c081175504084a2cb2156ad8d3db8b9e5612ed08a1f

See more details on using hashes here.

File details

Details for the file exec_helpers-3.4.0.post1-cp35-cp35m-manylinux1_i686.whl.

File metadata

  • Download URL: exec_helpers-3.4.0.post1-cp35-cp35m-manylinux1_i686.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: CPython 3.5m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 PyPy/5.10.1

File hashes

Hashes for exec_helpers-3.4.0.post1-cp35-cp35m-manylinux1_i686.whl
Algorithm Hash digest
SHA256 cb8366c6d4114fb4a83e743c23aa29911f5f1f073886ca4c6019d71cdcdf9b1d
MD5 89e3c783d86cfbff0858b6b3781c6801
BLAKE2b-256 ea25ee56409a6564ff8ae1e0543f9bc7b7f191391357ca65192dfb2f2f81e0aa

See more details on using hashes here.

Supported by

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