async def add_metadata_headers_with_maintainer(request: Request, call_next): response = await call_next(request) response.headers["X-App-Version"] = get_setting("APP_VERSION") response.headers["X-App-Valid-Till"] = get_setting("APP_VALID_DATE") response.headers["X-Maintainer"] = get_setting("APP_MAINTAINER") response.headers["X-Maintainer-Email"] = get_setting("APP_MAINTAINER_EMAIL") return response
def add_application_metadata(logger, method_name, event_dict): if 'app_version' not in event_dict: event_dict['app_version'] = get_setting('APP_VERSION') if 'app_valid_date' not in event_dict: event_dict['app_valid_date'] = get_setting('APP_VALID_DATE') return event_dict
async def add_metadata_headers_with_maintainer(request: Request, call_next): response = await call_next(request) response.headers['X-App-Version'] = get_setting('APP_VERSION') response.headers['X-App-Valid-Till'] = get_setting('APP_VALID_DATE') response.headers['X-Maintainer'] = get_setting('APP_MAINTAINER') response.headers['X-Maintainer-Email'] = get_setting('APP_MAINTAINER_EMAIL') return response
def add_application_metadata(logger, method_name, event_dict): if "app_version" not in event_dict: event_dict["app_version"] = get_setting("APP_VERSION") if "app_valid_date" not in event_dict: event_dict["app_valid_date"] = get_setting("APP_VALID_DATE") return event_dict
async def handle_http_exception(request: Request, exc: StarletteHTTPException) -> JSONResponse: headers = getattr(exc, "headers", None) body = {'detail': exc.detail} if get_setting('SHOW_MAINTAINER'): body['maintainer'] = get_setting('APP_MAINTAINER') body['maintainer_email'] = get_setting('APP_MAINTAINER_EMAIL') return JSONResponse(body, status_code=exc.status_code, headers=headers)
async def handle_http_exception(request: Request, exc: StarletteHTTPException) -> JSONResponse: headers = getattr(exc, "headers", None) body = {"detail": exc.detail} if get_setting("SHOW_MAINTAINER"): body["maintainer"] = get_setting("APP_MAINTAINER") body["maintainer_email"] = get_setting("APP_MAINTAINER_EMAIL") return JSONResponse(body, status_code=exc.status_code, headers=headers)
def initialize_logging(): timestamper = structlog.processors.TimeStamper(fmt="iso") shared_processors = [ structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, add_application_metadata, timestamper, structlog.processors.UnicodeDecoder(), ] pre_chain = shared_processors # Setup JSON-logging to stdout logging.captureWarnings(True) logging.config.dictConfig( { "version": 1, "disable_existing_loggers": False, "formatters": { "json": { "()": structlog.stdlib.ProcessorFormatter, "processor": structlog.processors.JSONRenderer(), "foreign_pre_chain": pre_chain, } }, "handlers": { "default": { "level": get_setting("LOG_LEVEL"), "class": "logging.StreamHandler", "formatter": "json", } }, "loggers": { "": { "handlers": ["default"], "level": get_setting("LOG_LEVEL"), "propagate": True, } }, } ) structlog.configure( processors=[structlog.stdlib.filter_by_level] + shared_processors + [structlog.stdlib.ProcessorFormatter.wrap_for_formatter], logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, )
def __init__(self): """ Specification of required fields for a weather data storage repository: - repository_folder: Contains the folder where the repository will be saved. Is set inside a main repository folder, and based on a sub-folder passed by the repository itself through the _get_repo_subfolder() function. - file_prefix: Contains a string with the file_prefix to use for all files within the repository. Is set from the repository itself. - runtime_limit: Contains the maximum time the update function of the repository is allowed to be running, in seconds. - first_day_of_repo: Contains a datetime indicating the oldest moment allowed to be stored in the repository. - last_day_of_repo: Contains a datetime indicating the newest moment allowed to be stored in the repository. - permanent_suffixes: Contains a list of suffixes that can be added to the repository files to indicate that the file should not be deleted. Any file not matching the prefix or having a suffix not matching this list will be deleted upon cleanup. - file_identifier_length: This is the length in characters that the unique identifier part of the filename takes up. Usually this is based on a datetime. """ self.repository_folder = Path(get_setting("REPO_FOLDER")).joinpath( self._get_repo_sub_folder()) self.logger = structlog.get_logger(__name__) self.repository_name = None self.file_prefix = None self.runtime_limit = 60 * 60 * 2 # seconds * minutes * hours (2 hours default) self.first_day_of_repo = None self.last_day_of_repo = None self.permanent_suffixes = None self.file_identifier_length = None
def initialize_prometheus_middleware(app, endpoint="/metrics"): """Start HTTP endpoint for Prometheus monitoring. Args: app: FastAPI app instance endpoint (str): URL at which the metrics should be available """ if get_setting("DEPLOYED"): logger.info(f'Enabling Prometheus endpoint on: "{endpoint}"') app.add_middleware(PrometheusMiddleware) app.add_route(endpoint, metrics)
def initialize_validation_middleware(app): valid_date = datetime.datetime.strptime(get_setting("APP_VALID_DATE"), "%Y-%m-%d") async def check_api_validity(request: Request, call_next): if datetime.datetime.now() > valid_date: response = await handle_http_exception(request, APIExpiredException()) else: response = await call_next(request) return response app.add_middleware(BaseHTTPMiddleware, dispatch=check_api_validity)
def initialize_metadata_header_middleware(app): async def add_metadata_headers(request: Request, call_next): response = await call_next(request) response.headers['X-App-Version'] = get_setting('APP_VERSION') response.headers['X-App-Valid-Till'] = get_setting('APP_VALID_DATE') return response async def add_metadata_headers_with_maintainer(request: Request, call_next): response = await call_next(request) response.headers['X-App-Version'] = get_setting('APP_VERSION') response.headers['X-App-Valid-Till'] = get_setting('APP_VALID_DATE') response.headers['X-Maintainer'] = get_setting('APP_MAINTAINER') response.headers['X-Maintainer-Email'] = get_setting('APP_MAINTAINER_EMAIL') return response show_maintainer = get_setting('SHOW_MAINTAINER') if show_maintainer: insertion_func = add_metadata_headers_with_maintainer else: insertion_func = add_metadata_headers app.add_middleware(BaseHTTPMiddleware, dispatch=insertion_func)
async def add_metadata_headers(request: Request, call_next): response = await call_next(request) response.headers['X-App-Version'] = get_setting('APP_VERSION') response.headers['X-App-Valid-Till'] = get_setting('APP_VALID_DATE') return response
initialize_metadata_header_middleware(app) initialize_validation_middleware(app) initialize_prometheus_middleware(app) origins = ["*"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Activate enabled API versions mount_api_version(app, v1) # Redirect users to the docs @app.get('/') def redirect_to_docs(): redirect_url = '/api/v1/sample/docs' # replace with docs URL or use app.url_path_for() return RedirectResponse(url=redirect_url) if __name__ == '__main__': # Run the application uvicorn.run(app, host=get_setting('NETWORK_INTERFACE'), port=get_setting('NETWORK_PORT'))