Squashed 'utils_v2/' content from commit ef9630d
git-subtree-dir: utils_v2 git-subtree-split: ef9630d728847764d4832ce8f3f956571901383a
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1007
File diff suppressed because it is too large
Load Diff
+164
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Thursday, 12th Sept., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To maintain all status codes in one place.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
from enum import Enum, unique
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
@unique
|
||||
class HttpCodes(Enum):
|
||||
|
||||
"""
|
||||
Commonly used standard HTTP status codes.
|
||||
Can be sent after an API Call is processed.
|
||||
Refer to: https://http.dev/status
|
||||
NOTE: THIS LIST IS NOT EXHAUSTIVE!
|
||||
"""
|
||||
|
||||
# 1XX - Informational:
|
||||
CONTINUE = 100 # .............. https://http.dev/102
|
||||
SWITCHING_PROTOCOLS = 101 # ... https://http.dev/101
|
||||
PROCESSING = 102 # ............ https://http.dev/102
|
||||
EARLY_HINTS = 103 # ........... https://http.dev/103
|
||||
|
||||
# 2XX - Success:
|
||||
SUCCESS = 200 # .................. https://http.dev/200
|
||||
CREATED = 201 # .................. https://http.dev/201
|
||||
ACCEPTED = 202 # ................. https://http.dev/202
|
||||
NON_AUTHORITATIVE_INFO = 203 # ... https://http.dev/203
|
||||
NO_CONTENT = 204 # ............... https://http.dev/204
|
||||
RESET_CONTENT = 205 # ............ https://http.dev/205
|
||||
PARTIAL_CONTENT = 206 # .......... https://http.dev/206
|
||||
MULTI_STATUS = 207 # ............. https://http.dev/207
|
||||
ALREADY_REPORTED = 208 # ......... https://http.dev/208
|
||||
THIS_IS_FINE = 218 # ............. https://http.dev/218
|
||||
IM_USED = 226 # .................. https://http.dev/226
|
||||
|
||||
# 3XX - Redirection:
|
||||
MULTIPLE_CHOICES = 300 # ..... https://http.dev/300
|
||||
MOVED_PERMANENTLY = 301 # .... https://http.dev/301
|
||||
MOVED_TEMPORARILY = 302 # .... https://http.dev/302
|
||||
SEE_OTHER = 303 # ............ https://http.dev/303
|
||||
NOT_MODIFIED = 304 # ......... https://http.dev/304
|
||||
USE_PROXY = 305 # ............ https://http.dev/305
|
||||
SWITCH_PROXY = 306 # ......... https://http.dev/306
|
||||
TEMPORARY_REDIRECT = 307 # ... https://http.dev/307
|
||||
PERMANENT_REDIRECT = 308 # ... https://http.dev/308
|
||||
|
||||
# 4XX - Client Errors:
|
||||
BAD_REQUEST = 400 # ..................... https://http.dev/401
|
||||
UNAUTHORIZED = 401 # .................... https://http.dev/401
|
||||
PAYMENT_REQUIRED = 402 # ................ https://http.dev/402
|
||||
FORBIDDEN = 403 # ....................... https://http.dev/403
|
||||
NOT_FOUND = 404 # ....................... https://http.dev/404
|
||||
METHOD_NOT_ALLOWED = 405 # .............. https://http.dev/405
|
||||
NOT_ACCEPTABLE = 406 # .................. https://http.dev/406
|
||||
PROXY_AUTH_REQUIRED = 407 # ............. https://http.dev/407
|
||||
REQUEST_TIMEOUT = 408 # ................. https://http.dev/408
|
||||
CONFLICT = 409 # ........................ https://http.dev/409
|
||||
GONE = 410 # ............................ https://http.dev/410
|
||||
LENGTH_REQUIRED = 411 # ................. https://http.dev/411
|
||||
PRECONDITION_FAILED = 412 # ............. https://http.dev/412
|
||||
PAYLOAD_TOO_LARGE = 413 # ............... https://http.dev/413
|
||||
URI_TOO_LONG = 414 # .................... https://http.dev/414
|
||||
UNSUPPORTED_MEDIA_TYPE = 415 # .......... https://http.dev/415
|
||||
PAGE_EXPIRED = 419 # .................... https://http.dev/419
|
||||
TOO_MANY_REQUESTS = 429 # ............... https://http.dev/429
|
||||
UNAVAILABLE_FOR_LEGAL_REASONS = 451 # ... https://http.dev/451
|
||||
INVALID_TOKEN = 498 # ................... https://http.dev/498
|
||||
CLIENT_CLOSED_REQUEST = 499 # ........... https://http.dev/499
|
||||
|
||||
# 5XX - Server Errors:
|
||||
INTERNAL_SERVER_ERROR = 500 # ........... https://http.dev/500
|
||||
NOT_IMPLEMENTED = 501 # ................. https://http.dev/501
|
||||
BAD_GATEWAY = 502 # ..................... https://http.dev/502
|
||||
SERVICE_UNAVAILABLE = 503 # ............. https://http.dev/503
|
||||
GATEWAY_TIMEOUT = 504 # ................. https://http.dev/504
|
||||
HTTP_VERSION_NOT_SUPPORTED = 505 # ...... https://http.dev/505
|
||||
VARIANT_ALSO_NEGOTIATES = 506 # ......... https://http.dev/506
|
||||
INSUFFICIENT_STORAGE = 507 # ............ https://http.dev/507
|
||||
LOOP_DETECTED = 508 # ................... https://http.dev/508
|
||||
BANDWIDTH_LIMIT_EXCEEDED = 509 # ........ https://http.dev/509
|
||||
WEB_SERVER_DOWN = 521 # ................. https://http.dev/521
|
||||
ORIGIN_IS_UNREACHABLE = 523 # ........... https://http.dev/523
|
||||
SERVICE_IS_OVERLOADED = 529 # ........... https://http.dev/529
|
||||
NETWORK_READ_TIMEOUT_ERROR = 598 # ...... https://http.dev/598
|
||||
NETWORK_CONNECT_TIMEOUT_ERROR = 599 # ... https://http.dev/599
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@unique
|
||||
class StatusCodes(Enum):
|
||||
|
||||
"""
|
||||
To be used internally within the context of your service. Customize these to match your service.
|
||||
The format is: (SUCCESS_INDICATOR, INTERNAL_NUMERIC_CODE, HTTP_CODE)
|
||||
Example: (True, 1, 200)
|
||||
"""
|
||||
|
||||
# Legacy Codes:
|
||||
OK = (True, 1, HttpCodes.SUCCESS.value)
|
||||
FAILED = (False, 0, HttpCodes.INTERNAL_SERVER_ERROR.value)
|
||||
PARTIAL_SUCCESS = (True, 2, HttpCodes.PARTIAL_CONTENT.value)
|
||||
PARTIAL_FAILURE = (False, 3, HttpCodes.PARTIAL_CONTENT.value)
|
||||
|
||||
# Authentication Codes:
|
||||
LOGGED_IN_SUCCESSFULLY = (True, 200, HttpCodes.SUCCESS.value)
|
||||
LOGIN_FAILED = (False, 201, HttpCodes.UNAUTHORIZED.value)
|
||||
INVALID_SESSION_TOKEN = (False, 202, HttpCodes.UNAUTHORIZED.value)
|
||||
AUTHENTICATION_DETAILS_INCOMPLETE = (False, 203, HttpCodes.BAD_REQUEST.value)
|
||||
|
||||
# Authorization Codes:
|
||||
AUTHORIZED_SUCCESSFULLY = (True, 300, HttpCodes.SUCCESS.value)
|
||||
NOT_ALLOWED = (False, 301, HttpCodes.FORBIDDEN.value)
|
||||
AUTHORIZATION_DETAILS_INCOMPLETE = (False, 302, HttpCodes.BAD_REQUEST.value)
|
||||
AUTHORIZATION_FAILED = (False, 303, HttpCodes.UNAUTHORIZED.value)
|
||||
|
||||
# General failures:
|
||||
DOWN_FOR_MAINTENANCE = (False, 800, HttpCodes.SERVICE_UNAVAILABLE.value)
|
||||
UNKNOWN_ERROR = (False, 801, HttpCodes.INTERNAL_SERVER_ERROR.value)
|
||||
DATA_INCOMPLETE = (False, 802, HttpCodes.BAD_REQUEST.value)
|
||||
HEADERS_INCOMPLETE = (False, 803, HttpCodes.BAD_REQUEST.value)
|
||||
FILES_MISSING = (False, 804, HttpCodes.BAD_REQUEST.value)
|
||||
CLIENT_CLOSED_REQUEST = (False, 805, HttpCodes.CLIENT_CLOSED_REQUEST.value)
|
||||
|
||||
# Validation failure:
|
||||
DATA_VALIDATION_FAILURE = (False, 900, HttpCodes.BAD_REQUEST.value)
|
||||
RATE_LIMIT_EXCEEDED = (False, 901, HttpCodes.TOO_MANY_REQUESTS.value)
|
||||
@@ -0,0 +1,198 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
Sharvil J Daiya
|
||||
|
||||
DATE:
|
||||
|
||||
Saturday, 28th Sept., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To have one place from where several metrics are measured using easy to use context managers.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# To measure time:
|
||||
import time
|
||||
|
||||
# To capture metrics for Prometheus:
|
||||
from prometheus_client import Counter, Summary, Gauge
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# Define the Prometheus metrics.
|
||||
# FOR THE MICROSERVICE AS A WHOLE:
|
||||
WORKER_COUNT = Counter(
|
||||
name = "ms_workers_active_total",
|
||||
documentation = "The number of threads for the microservice being monitored.",
|
||||
labelnames = ["project_name", "service_name", "host_name"]
|
||||
)
|
||||
|
||||
# Define the Prometheus metrics.
|
||||
# FOR INDIVIDUAL API ENDPOINTS:
|
||||
REQUEST_LATENCY = Summary(
|
||||
name = "http_request_latency_seconds",
|
||||
documentation = "Latency of HTTP requests in seconds.",
|
||||
labelnames = ["method", "endpoint", "http_status"]
|
||||
)
|
||||
TOTAL_REQUEST_COUNT = Counter(
|
||||
name = "http_requests_total",
|
||||
documentation = "Total HTTP requests.",
|
||||
labelnames = ["method", "endpoint", "http_status"]
|
||||
)
|
||||
LIVE_REQUEST_COUNT = Gauge(
|
||||
name = "http_requests_live_total",
|
||||
documentation = "To check if an API endpoint is being served right now.",
|
||||
labelnames = ["endpoint"]
|
||||
)
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** CLASSES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
class MetricsAPI:
|
||||
|
||||
"""
|
||||
Use this class through its context manager to automatically measure all the metrics in one place.
|
||||
This was originally created to measure the performance of API endpoints made in Quart, but it should work with
|
||||
other frameworks as well.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
method = None,
|
||||
endpoint = None,
|
||||
raise_exception = False
|
||||
):
|
||||
|
||||
# Make provisions for things to note.
|
||||
# NOTE: THESE MUST BE SET FROM OUTSIDE:
|
||||
self.method = method
|
||||
self.endpoint = endpoint
|
||||
self.http_code = None
|
||||
self.__raise_exception = raise_exception
|
||||
|
||||
async def __aenter__(self):
|
||||
|
||||
# Note down the start time immediately:
|
||||
self.__start_ts = time.perf_counter()
|
||||
self.__cpu_start_ts = time.process_time()
|
||||
|
||||
# Note down the metrics:
|
||||
LIVE_REQUEST_COUNT.labels(self.endpoint).inc(1)
|
||||
|
||||
# Setup done:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_value, traceback):
|
||||
|
||||
# Note down the metrics:
|
||||
LIVE_REQUEST_COUNT.labels(self.endpoint).dec(1)
|
||||
|
||||
TOTAL_REQUEST_COUNT.labels(
|
||||
self.method,
|
||||
self.endpoint,
|
||||
self.http_code
|
||||
).inc()
|
||||
|
||||
REQUEST_LATENCY.labels(
|
||||
self.method,
|
||||
self.endpoint,
|
||||
self.http_code
|
||||
).observe(time.perf_counter() - self.__start_ts)
|
||||
|
||||
# Handle the exception as per the user's preference:
|
||||
return False if self.__raise_exception else True
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
from prometheus_client import generate_latest
|
||||
|
||||
async def simulate_endpoint():
|
||||
|
||||
async with MetricsAPI(
|
||||
method = random.choice(["GET", "POST"]),
|
||||
endpoint = f"https://my.domain.com/api/{random.choice([0, 1, 2, 3])}"
|
||||
) as metrics:
|
||||
|
||||
# Simulate some action on some endpoint:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
# Note down the values:
|
||||
metrics.http_code = 200
|
||||
|
||||
async def main():
|
||||
|
||||
print("Simulating endpoints...")
|
||||
tasks = [simulate_endpoint() for _ in range(250)]
|
||||
await asyncio.gather(*tasks)
|
||||
print("Done!")
|
||||
|
||||
print("METRICS:")
|
||||
print(generate_latest().decode())
|
||||
|
||||
asyncio.run(main())
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Thursday, 12th Sept., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To have a structure to the response sent from the API calls.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing import Any, Optional, List
|
||||
|
||||
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** CLASSES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
class ResponseModel(BaseModel):
|
||||
|
||||
status_code: StatusCodes
|
||||
message: Optional[str | List] = None
|
||||
data: Optional[Any] = None
|
||||
seconds: Optional[float | int] = None
|
||||
log_id: Optional[str] = None
|
||||
http_code: Optional[HttpCodes] = None
|
||||
api_version: Optional[str] = None
|
||||
|
||||
def for_quart(self):
|
||||
|
||||
# Construct the basic structure:
|
||||
response_dict = {
|
||||
"status": 1 if self.status_code.value[0] else 0,
|
||||
"code": self.status_code.value[1],
|
||||
"message": self.message or self.status_code.name.replace("_", " ").lower(),
|
||||
"data": self.data,
|
||||
"apiVer": self.api_version
|
||||
}
|
||||
|
||||
# Now add the additional fields:
|
||||
if self.seconds is not None: response_dict["seconds"] = self.seconds
|
||||
if self.log_id is not None: response_dict["logId"] = self.log_id
|
||||
|
||||
# Figure out the HTTP code:
|
||||
response_http_code = self.http_code.value if self.http_code is not None else self.status_code.value[2]
|
||||
|
||||
# Done here:
|
||||
return response_dict, response_http_code
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
my_response = ResponseModel(
|
||||
status_code = StatusCodes.RATE_LIMIT_EXCEEDED
|
||||
)
|
||||
my_response.log_id = "abc123"
|
||||
|
||||
print(my_response.for_quart())
|
||||
Reference in New Issue
Block a user