Skip to main content

Log library to establish communication with FTP server and AWS CloudWatch; as well as to publish log message to these platforms.

Project description

Python Application Logging Module

The centralized application logging library.

Logging Data Level

  • FATAL: Log the application/ solution/ API state or behaviours that causes the system to terminate unintentionally; or have to cause the system to halt.
  • ERROR: Log the application/ solution/ API state or behaviours that causes the system response abnormally; or is returning not expected out; but the system is still running.
  • DEBUG: Log the application/ solution/ API behaviours or state change.
  • INFO: Log the AI model performance or output.

Unique ID Generation

Please do use uuid4 for generating the random unique ID.

import uuid 
from loris_log.boto3Client import Boto3Client

# generating the random uu_id for each calling session.
id = uuid.uuid4()

Otherwise use uuid5 for fixed id generation.

import uuid 

# generating the fixed uu_id for each calling session.
id = uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')

Log Message onto CloudWatch Examples

Construct the Error Log Message

This shows the example of constructing the error log message for CloudWatch.

def set_error_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of an application/ remote host IP address/ service.
  • message (list): The error message.
from loris_log.customLog import CustomLog
import uuid

# get the random uuid generated
id = uuid.uuid4()
# define the error log message as the string list.
error_message = ["[400]", "Service not found."]
# define the name (string), oftenly is the IP address of remote host, or the application name
name = "192.168.0.0"

# then, construct the corresponding error log message by initialize set_error_message
# function, and passing in the id (uuid::UUID), name (string), and error_message (list).
error_log = CustomLog.set_error_message(id, name, error_message)

Construct the Debug Log Message

This shows the example of constructing the debug log message for CloudWatch.

def set_debug_message(uu_id, name, message):

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of the application/ remote host IP address/ service.
  • message (list): The debug message.
from loris_log.customLog import CustomLog
import uuid 

# get the random uuid
id = uuid.uuid4()
# define the debug message as the string list
debug_message = ["[200]", "Service ok"]
# define the name (string), oftenly is the IP address of remote host, or the application name
name = "192.168.0.0"

# then, construct the corresponding debug log message by calling set_debug_message function,
# and passing in the id (uuid::UUID), name (string), and debug_message (string).
debug_log = CustomLog.set_debug_message(id, name, debug_message)

Construct the Fatal Log Message

This shows the example of constructing the fatal log message for CloudWatch.

def set_fatal_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of the application/ remote host IP address/ service.
  • message (list): The fatal message.
from loris_log.customLog import CustomLog 
import uuid 

# get the random uuid
id = uuid.uuid4()
# define the fatal message as the string list 
fatal_message = ["[500", "Service terminated unexpectedly"]
# define the name (string), oftenly is the IP address of remote host, or the application name.
name = "192.168.0.0"

# then, construct the corresponding fatal log message by calling the set_fatal_message
# function and passing it id (uuid::UUID), name (string), and message (list).
fatal_log = CustomLog.set_fatal_message(id, name, fatal_message)

Construct the Info Log Message

This shows the example of constructing the info log message for CloudWatch.

def set_info_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of the application/ remote IP address/ service.
  • message (list): The info message.
from loris_log.customLog import CustomLog 
import uuid 

# get the random uuid generated.
id = uuid.uuid4()
# define the fatal message as the string list.
info_message = ["[200]", "the accuracy is 0.99"]
# define the name (string), oftenly is the IP address of remote host, or the application name.
name = "192.168.0.0"

# then, construct the corresponding info log message via calling set_info_message function.
# by passing in the id (uuid::UUID), name (string), and example_fatal_message (string).
info_log = CustomLog.set_info_message(id, name, fatal_message)

Pushing Log Message onto the CloudWatch

This shows the example of establishing the connection to the AWS CloudWatch service via the following method:

def Boto3Client(region_name, aws_key, aws_secret)

Arguments:

  • region_name (string): The AWS service region name. Refers here region_name.
  • aws_key (string): The AWS service role access key.
  • aws_secret (string): The AWS service role secret key.

Then, pushing the constructed message onto the CloudWatch via the set_log_message function, after defining its log group and log stream:

def set_log_message(log_group_name, log_stream_name, log_message)

Arguments:

  • log_group_name (string): The name of the log group.
  • log_stream_name (string): The name of the log stream.
  • log_message (list): A list of application/ service/ api message.

Below shows the complete example of pushing an log message onto the AWS CloudWatch.

import uuid
from loris_log.customLog import CustomLog
from loris_log.boto3Client import Boto3Client

# constuct the log message uses the above elaborated approach. The example
# here creates a sample fatal log message.
# ========================================================================
# get the random uuid
id = uuid.uuid4()
# define the fatal message as the string list 
fatal_message = ["[500]", "Service terminated unexpectedly"]
# define the log name (string), oftenly is the IP address of remote host, or the application name.
name = "192.168.0.0"

# then, construct the corresponding fatal log message
fatal_log = CustomLog.set_fatal_message(id, name, fatal_message)

# Subsequently, push the created fatal log message onto the Cloudwatch
# ###############################################################################
# initialize the boto3 client by first define the log group(string), log stream (string), region (string),
# as well as the key (string) and secret (string). All in form of string.
log_group = ""
log_stream = ""
aws_region = ""
aws_key = ""
aws_secret = ""
# establish the boto3 connection by initialize Boto3Client instance, then
# passing the aws_region (string), aws_key (string) and aws_secret (string).
client = Boto3Client(aws_region, aws_key, aws_secret)
# then push the log message via following set_log_message method by passing them
# log_group name (string), log_stream name (string, and the log_messsage (list).
client.set_log_message(log_group, log_stream, fatal_log)

Log Message onto Remote Server Via FTP Examples

Construct the Debug Log Message

This shows the example of constructing the debug log message onto the remote FTP server using set_debug_message function.

def set_debug_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of an application/ remote host IP address/ service.
  • message (list): The debug message.
from loris_log.customLog import CustomLog 

# randomly generated an id using uuid library
id = uuid.uuid4()
# then define the debug log message that is a list
debug_log = ["[200]", "OK"]
# and define the log name that is a string, which usually is the application name
# or the remote host IP address.
name = "192.168.1.1" 

# afterwards, build the complete debug log message string using set_debug_message
# function by passing the id (id::UUID), name (string), and debug_log (list).
debug_message = CustomLog.set_debug_message(id, name, debug_log)

Construct the Info Log Message

This shows the example of constructing the info log message onto the remmote FTP server via the set_info_message function.

def set_info_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of an application/ remote host IP address/ service.
  • message (list): The info message.
from loris_log.customLog import CustomLog
import uuid 

# get the random uuid generated
id = uuid.uuid4()
# define the info log message as the string list
info_message = ["[200]", "result is 0.9"]
# define the name (string), oftenly is an IP address of remote host, or the application name
name = "192.168.0.0"

# then, construct the corresponding info log message by initialize the set_info_message
# function, and passing in the id (uuid::UUID), name (string), and error_message(list)
info_log = CustomLog.set_info_message(id, name, info_message)

Construct the Error Log Message

This shows the example of constructing the error log message onto the FTP remote server via the set_error_message function.

def set_error_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of an application/ remote host IP address/ service.
  • message (list): The error message.
from loris_log.customLog import CustomLog
import uuid

# get the random uuid generated
id = uuid.uuid4()
# define the error log message as the string list.
error_message = ["[400]", "Service not found."]
# define the name (string), oftenly is the IP address of remote host, or the application name
name = "192.168.0.0"

# then, construct the corresponding error log message by initialize set_error_message
# function, and passing in the id (uuid::UUID), name (string), and error_message (list).
error_log = CustomLog.set_error_message(id, name, error_message)

Construct the Fatal Log Message

This shows the example of constructing the fatal log message for CloudWatch via the set_fatal_message(uu_id, name, message) function.

def set_fatal_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of application/ remote host IP address/ service.
  • message (list): The fatal message.
from loris_log.customLog import CustomLog 
import uuid 

# get the random uuid
id = uuid.uuid4()
# define the fatal message as the string list 
fatal_message = ["[500", "Service terminated unexpectedly"]
# define the name (string), oftenly is the IP address of remote host, or the application name.
name = "192.168.0.0"

# then, construct the corresponding fatal log message by calling the set_fatal_message
# function and passing it id (uuid::UUID), name (string), and message (list).
fatal_log = CustomLog.set_fatal_message(id, name, fatal_message)

Construct the Log for the Sensor Data

This shows the example of constructing the log message from some embedded device data via the set_ftp_log_data function.

def set_ftp_log_data(uu_id, starttime, endttime, result, groundtruth)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • starttime (string): The start time of an operation. Conversion from datetime.datetime.now().
  • endtime (string): The end time of an operation. Conversion from datetime.datetime.now().
  • result (string): The result of the embedded device/ system/ model etc. Conversion from double\ float\ long.
  • groundtruth (string): The result of embedded device/ system/ model etc. Conversion from double\ float\ long.
import datetime 
import uuid
from loris_log.customLog import CustomLog

# generate the uuid
id = uuid.uuid4()
# define the start time
starttime = datetime.datetime.now().strftime("%H:%M:%S")
# define the end time
endtime = datetime.datetime.now().strftime("%H:%M:%S")
# define the result
result = str(32)
# define the groundtruth
groundtruth = str(3)

# construct the log data message
data_log = CustomLog.set_ftp_log_data(id, starttime, endtime, result, groundtruth)

Pushing Info Log Message onto Remote FTP Server

This example shows the approach to push the log message onto the remote FTP server. It first involves construction of the relevant log message. This example, for instance, create the info log message via the set_info_message function.

def set_info_message(uu_id, name, message)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • name (string): The name of an application/ remote host IP address/ service.
  • message (list): The info message.
from loris_log.customLog import CustomLog
import uuid 

# get the random uuid generated
id = uuid.uuid4()
# define the info log message as the string list
info_message = ["[200]", "result is 0.9"]
# define the name (string), oftenly is an IP address of remote host, or the application name
name = "192.168.0.0"

# then, construct the corresponding info log message by initialize the set_info_message
# function, and passing in the id (uuid::UUID), name (string), and error_message(list)
info_log = CustomLog.set_info_message(id, name, info_message)

Then, attempt to establish the communication with the target remote FTP server by providing the FTP server hostname (string), port number (string), username (string), as well as the password (string) via the FTPClient instance

def FTPClient(hostname, port_num, username, password)

Arguments:

  • hostname (string): FTP server's hostname.
  • port_num (integer): FTP server's port number.
  • username (string): FTP server's username.
  • password (string): FTP server's password.

as elaborated in the code snippet below.

from loris_log.ftpClient import FtpClient

hostname = ""
port_num = 22
username = ""
password = ""

# Establish the connection to the remote FTP server
client = FtpClient(hostname, port_num, username, password)

After the connection was successfully established. It is time to publish the corresponding log message onto the remote FTP server, based on the define file path, and filename via the set_ftp_log_file method.

def set_ftp_log_file(directory_name, file_name, message)

Arguments:

  • directory_path (string): The path to the log file.
  • file_name (string): The log file name (without extension).
  • message (string): The log message
# define the path for the log file, must follow the defined format
file_path = "log/testing/data"
# then define the log file name and its file extensions
filename = "sample"

# Push the log file onto the remote FTP server. New log will
client.set_ftp_log_file(file_path, filename, info_log)

If the log file (.log) was previously not created, please remember create the relevant folders and file using create_ftp_log_file

def create_ftp_log_file(file_path, filename):

  • file_path: the path to the log file.
  • filename: the name of log file without extension.
file_path = "log_testing/data"
filename = "sample"

client.create_ftp_log_file(file_path, filename)

This shows the complete code example

from loris_log.customLog import CustomLog
from loris_log.ftpClient import FtpClient
import uuid

# get the random uuid generated
id = uuid.uuid4()
# define the info log message as the string list
info_message = ["[200]", "result is 0.9"]
# define the name (string), oftenly is an IP address of remote host, or the application name
name = "192.168.0.0"
# then, construct the corresponding info log message by initialize the set_info_message
# function, and passing in the id (uuid::UUID), name (string), and error_message(list)
info_log = CustomLog.set_info_message(id, name, info_message)

# define the FTP server hostname
hostname = ""
# define the FTP server port number
port_num = 22
# define the FTP server username 
username = ""
# define the FTP server password
password = ""
# Establish the connection to the remote FTP server
client = FtpClient(hostname, port_num, username, password)


# define the path for the log file, must follow the defined format
file_path = "log/testing/data"
# then define the log file name and its file extensions
filename = "sample"

# If previously did not create the log message, create the relevant
# log file first
# @note: ignore this step write to existing log file
client.create_ftp_log_file(file_path, filename)

# Push the log file onto the remote FTP server. New log will
client.set_ftp_log_file(file_path, filename, info_log)

Please uses set_error_message, set_info_message, set_fatal_message, or set_debug_message method to log the error, info, fatal or debug message.

Example of Pushing Sensor Data to Remote FTP Server

This example shows the example of pushing sensor data as log message onto the remote FTP server.

First, construct the log message using the sensor data via the set_ftp_log_data method.

def set_ftp_log_data(uu_id, starttime, endtime, result, groundtruth)

Arguments:

  • uu_id (uuid::UUID): The generated uuid.
  • starttime (string): The start time of an operation. Conversion from datetime.datetime.now().
  • endtime (string): The end time of an operation. Conversion from datetime.datetime.now().
  • result (string): The result of the sensors, embedded device, system, model etc. Conversion from double/ float/ long.
  • groundtruth (string): The result of the sensors, embedded device, system, model etc. Conversion from double/ float/ long.
import datetime 
import uuid
from loris_log.customLog import CustomLog

# generate the uuid
id = uuid.uuid4()
# define the start time
starttime = datetime.datetime.now().strftime("%H:%M:%S")
# define the end time
endtime = datetime.datetime.now().strftime("%H:%M:%S")
# define the result
result = str(32)
# define the groundtruth
groundtruth = str(3)

# construct the log data message
data_log = CustomLog.set_ftp_log_data(id, starttime, endtime, result, groundtruth)

Once the log message was crafted, it is time to attempt to establish communication with the remote FTP server using the FTPClient method.

def FTPClient(hostname, port_num, username, password)

Arguments:

  • hostname (string): FTP server's hostname.
  • port_num (integer): FTP server's port number.
  • username (string): FTP server's username.
  • password (string): FTP server's password.

as elaborated in the code snippet below.

from loris_log.ftpClient import FtpClient

hostname = ""
port_num = 22
username = ""
password = ""

# Establish the connection to the remote FTP server
client = FtpClient(hostname, port_num, username, password)

Then, defining the path and filename of the corresponding log file, and have the data pushing onto an log file of a FTP server using the set_ftp_log_data method.

def set_ftp_log_data(directory_path, file_name, log_data)

Arguments:

  • directory_path (string): The path to the log file.
  • file_name (string): The name of the log file without extension.
  • log_data (byte): The data to be logged.
# define the path to the log file
directory_path = "/log_testing/data"
# define the log file with its extension
file_name = "log"
# push the log data onto the remote FTP server.
client.set_ftp_log_data(directory_path, file_name, data_log)

If the log file (csv file) was not previously created. Please create one using the method create_ftp_log_data.

def create_ftp_log_data(file_path, filename):

  • file_path: The directory of the log file.
  • filename: The name of log file without extension (without extension).

The complete example is as demonstrated below.

import datetime 
import uuid
from loris_log.customLog import CustomLog
from loris_log.ftpClient import FtpClient

# generate the uuid
id = uuid.uuid4()
# define the start time
starttime = datetime.datetime.now().strftime("%H:%M:%S")
# define the end time
endtime = datetime.datetime.now().strftime("%H:%M:%S")
# define the result
result = str(32)
# define the groundtruth
groundtruth = str(3)
# construct the log data message
data_log = CustomLog.set_ftp_log_data(id, starttime, endtime, result, groundtruth)

# define FTP server hostname
hostname = ""
# define FTP server port number
port_num = 22
# define FTP server username
username = ""
# define FTP server password
password = ""
# Establish the connection to the remote FTP server
client = FtpClient(hostname, port_num, username, password)

# define the path to the log file
directory_path = "/log_testing/data"
# define the log file with its extension
file_name = "log"

# @optional, create the log file if it was never created. Ignore this line, if
# the file was existed.
client.create_ftp_log_data(directory_path, file_name)

# push the log data onto the remote FTP server.
client.set_ftp_log_data(directory_path, file_name, data_log)

Example of Constructing the Info Log Message for A Sensor

This example shows the approach to construct the info log message for an sensor using its id whose often is a string of text. This is achieve via the set_sensor_info_message method as elaborated below.

def set_sensor_info_message(sensor_id, name, message)

  • sensor_id (string): The sensor's serial number.
  • name (string): The name of the application/ service or API.
  • message (list): A collection of the info log message.

The usage of this method is as presented in code snippet below.

from log_loris.customLog import CustomLog

sensor_info_log = CustomLog.set_sensor_info_message("[camera-01-2300001]", "[my application]", ["[200]", "OK"])

Example of Constructing the Debug Log Message for A Sensor

This example shows the approach to construct the debug log message for an sensor using its id whose often is a string of text. This is achieve via the set_sensor_debug_message method as elaborated below.

def set_sensor_debug_message(sensor_id, name, message)

  • sensor_id (string): The sensor's serial number.
  • name (string): The name of the application/ service or API.
  • message (list): A collection of the info log message.

The usage of this method is as presented in code snippet below:

from log_loris.customLog import CustomLog 

sensor_debug_log = CustomLog.set_sensor_debug_message("[camera-01-2300001]", "[my application]", ["[200]", "OK"])

Example of Constructing the Error Log Message for A Sensor

This example shows the approach to construct the error log message for an sensor using its id whose often is a string of text. This is achieve via the set_sensor_error_message method as elaborated below.

def set_sensor_error_message(sensor_id, name, message)

  • sensor_id (string): The sensor's serial number.
  • name (string): The name of the application/ service or API.
  • message (list): A collection of the error log message.

The usage of this method is as presented in code snippet below:

from loris_log.customLog import CustomLog

sensor_error_log = CustomLog.set_sensor_error_message("[camera-01-2300001]", "[my application]", ["[200]", "OK"])

Example of Constructing the Fatal Log Message for A Sensor

This example shows the approach to construct the fatal log message for an sensor using its id whose often is a string of text. This is achieve via the set_sensor_fatal_message method as elaborated below.

def set_sensor_fatal_message: The sensor's serial number.

  • sensor_id (string): The sensor's serial number.
  • name (string): The name of the applicatioon/ service or API.
  • message (list): A collection of the error log message.

The usage of this method is as presented in code snippet below:

from loris_log.customLog import CustomLog

sensor_fatal_log = CustomLog.set_sensor_fatal_message("[camera-01-2300001]", "[my application]", ["[200]", "OK"])

Example of Publishing Sensor Info Message onto CloudWatch

The initial step requires establishing the connection onto the CloudWatch via the Boto3Client as elaborated below.

Boto3Client(aws_region, aws_key, aws_secret)

  • aws_region (string): The AWS region.
  • aws_log_group (string): The AWS CloudWatch log group name.
  • aws_log_stream (string): The AWS CloudWatch log stream name.

This is follow by constructing the relevant log message using set_Sensor_info_message.

def set_sensor_info_message(sensor_id, name, message)

  • sensor_id: The sensor's serial number.
  • name: The name of the application/ service or API.
  • message (list): A collection of the info log message.

Follows by pushing the log on the AWS CloudWatch.

def set_log_message(log_group, log_stream, info_log)

  • log_group (string): The AWS CloudWatch log group name.
  • log_stream (string): The AWS CloudWatch log stream name.
  • info_log (string): The info log message.

The complete codes snippet is as below:

from loris_log.boto3Client import BotoClient 
from loris_log.customLog immport CustomLog 

# First, establish the connection to the AWS CloudWatch.
aws_region = ""
aws_log_group = ""
aws_log_stream = ""
client = Boto3Client(aws_region, aws_log_group, aws_log_stream)

# Then, construct the relevant log message; in this example
# a info log message for a sensor
info_message = CustomLog.set_sensor_info_message(
    "[camera-01-2300001]", "[my-app]", ["[200]", "OK"]
)

# Lastly, publishing the log message onto the CloudWatch
log_group = ""
log_stream = ""
client.set_log_message(log_group, log_stream, info_message)

Example of Publishing the Info Log Message onto FTP server.

This demonstrates the approach of how to publishing a sensor log message onto a remote FTP server.

This requires user first establishes the connection to a remote FTP server using the FtpClient method

def FtpClient(hostname, port_num, username, password):

  • hostname (string): The FTP server hostname (IP address).
  • port_nun (int): The FTP server port number.
  • username (string): The FTP server username.
  • password (string): The FTP server password.

Then, it is follows by creating the info log message for the sensor using the set_sensor_info_message method.

**def set_sensor_info_message(sensor_id, name, message)

  • sensor_id (string): The sensor's id.
  • name (string): The application/ API name.
  • message (list): The sensor's log message.

Lastly, publishing the log message and write it onto a log file via set_ftp_log_file method.

def set_ftp_log_file(file_path, filename, log_message)

  • file_path (string): The directory path of the log file.
  • filename (string): The log file's file name.
  • log_message (list): The sensor kog message.

If the log file (.log) was never created, create one first using the method create_ftp_log_file.

def create_ftp_log_file(file_path, filename):

  • file_path: The path to the log file.
  • filename: The name of log file without extension.

The complete example code is as presented below:

from loris_log.ftpClient import FtpClient
from loris_log.customLog import CustomLog

hostname = ""
port_num = 00
username = ""
password = ""
# establish connction 
client = FtpClient(hostname, port_num, username, password)

# Then construct the info log message for the sensor
info_message = CustomLog.set_sensor_info_message(
    "camera01-E11]]",
    "[my-app]", ["[200]", "OK"]
)

# Lastly push onto the FTP server.
file_path = "log/testing/data"
filename = "sample"

# Optional, create the file if log file not existed
# Otherwise, ignore this line
client.create_ftp_log_file(file_path, filename)

# push the log message onto the log file.
client.set_ftp_log_file(file_path, filename, info_message)

Project details


Download files

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

Source Distribution

loris_log-0.2.2.post3.tar.gz (119.9 kB view details)

Uploaded Source

Built Distribution

loris_log-0.2.2.post3-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file loris_log-0.2.2.post3.tar.gz.

File metadata

  • Download URL: loris_log-0.2.2.post3.tar.gz
  • Upload date:
  • Size: 119.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.12

File hashes

Hashes for loris_log-0.2.2.post3.tar.gz
Algorithm Hash digest
SHA256 48508b5f4fecf6e0b24c95b7759f791887ceacf05b0ede5bb54fa774d39415b0
MD5 273009908dc194e26ced28e1eca5a74a
BLAKE2b-256 3d9044f668ce626c5284043c44735c5a9ef54d455ecb2dd86c2bc10944e773c4

See more details on using hashes here.

File details

Details for the file loris_log-0.2.2.post3-py3-none-any.whl.

File metadata

File hashes

Hashes for loris_log-0.2.2.post3-py3-none-any.whl
Algorithm Hash digest
SHA256 7da75c37917d635b9efd2381c848907cc962c2cdc76878fe070280640c1e50e7
MD5 15d26d3945815391eb2e0aa9c166db7b
BLAKE2b-256 4a5a1ac65744a7a1bdd3e78018cfeede0e07abaebed34d21871e14080faf6f17

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