From de249d2448246b27090123326ec1a69d8c588863 Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Mon, 23 May 2022 22:19:28 +0530 Subject: [PATCH] Add logger --- Cargo.lock | 19 ++++++++++++++++ Cargo.toml | 2 ++ README.md | 3 ++- docs/api.md | 4 ++-- integration_tests/base_routes.py | 11 +++------ integration_tests/build/index.html | 11 +++++++++ robyn/__init__.py | 36 ++++++++++++++++++++++++------ robyn/argument_parser.py | 14 ++++++++++++ robyn/argument_parser.pyi | 24 ++++++++++++++++---- robyn/dev_event_handler.py | 9 +++----- robyn/processpool.py | 4 ++-- robyn/router.py | 1 - src/executors/mod.rs | 7 +++--- src/request_handler/mod.rs | 9 ++++---- src/routers/web_socket_router.rs | 3 ++- src/server.rs | 29 +++++++++++++----------- src/shared_socket.rs | 5 +++-- src/web_socket_connection.rs | 13 ++++++----- 18 files changed, 144 insertions(+), 60 deletions(-) create mode 100644 integration_tests/build/index.html diff --git a/Cargo.lock b/Cargo.lock index 7df7e4825..b3117f46a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,12 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +[[package]] +name = "arc-swap" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" + [[package]] name = "askama_escape" version = "0.10.3" @@ -1122,6 +1128,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "pyo3-log" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6087e5025c5d7d6ed6294c68f83d9f7965c2b3fd3a726ecc1c22dbe4af7390f4" +dependencies = [ + "arc-swap", + "log", + "pyo3", +] + [[package]] name = "pyo3-macros" version = "0.14.5" @@ -1223,9 +1240,11 @@ dependencies = [ "dashmap", "futures", "futures-util", + "log", "matchit", "pyo3", "pyo3-asyncio", + "pyo3-log", "serde", "serde_json", "socket2", diff --git a/Cargo.toml b/Cargo.toml index 843aa3464..b43739976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ uuid = { version = "0.8", features = ["serde", "v4"] } serde = "1.0.136" serde_json = "1.0.79" futures = "0.3.21" +pyo3-log = "0.4.1" +log = "0.4.17" [features] # Defines a feature named `webp` that does not enable any other features. diff --git a/README.md b/README.md index b8d912629..d8e4e9a6c 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ optional arguments: -h, --help show this help message and exit --processes PROCESSES : allows you to choose the number of parallel processes --workers WORKERS : allows you to choose the number of workers - --dev DEV : this flag gives the option to enable hot reloading or not + --dev DEV : this flag gives the option to enable hot reloading or not and also sets the default log level to debug + --log-level LEVEL : this flag allows you to set the log level ``` diff --git a/docs/api.md b/docs/api.md index 988319374..d0daa26b7 100644 --- a/docs/api.md +++ b/docs/api.md @@ -183,13 +183,13 @@ You can add startup and shutdown events in robyn. These events will execute befo ```python3 async def startup_handler(): - logger.log(logging.INFO, "Starting up") + print("Starting up") app.startup_handler(startup_handler) @app.shutdown_handler def shutdown_handler(): - logger.log(logging.INFO, "Shutting down") + print("Shutting down") ``` ## WebSockets diff --git a/integration_tests/base_routes.py b/integration_tests/base_routes.py index ca718788f..e5d5b76ee 100644 --- a/integration_tests/base_routes.py +++ b/integration_tests/base_routes.py @@ -2,10 +2,6 @@ import asyncio import os import pathlib -import logging - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) app = Robyn(__file__) websocket = WS(app, "/web_socket") @@ -156,12 +152,12 @@ def blocker(): async def startup_handler(): - logger.log(logging.INFO, "Starting up") + print("Starting up") @app.shutdown_handler def shutdown_handler(): - logger.log(logging.INFO, "Shutting down") + print("Shutting down") @app.get("/redirect") @@ -178,10 +174,9 @@ async def redirect_route(request): ROBYN_URL = os.getenv("ROBYN_URL", "0.0.0.0") app.add_header("server", "robyn") current_file_path = pathlib.Path(__file__).parent.resolve() - os.path.join(current_file_path, "build") app.add_directory( route="/test_dir", - directory_path=os.path.join(current_file_path, "build/"), + directory_path=os.path.join(current_file_path, "build"), index_file="index.html", ) app.startup_handler(startup_handler) diff --git a/integration_tests/build/index.html b/integration_tests/build/index.html new file mode 100644 index 000000000..1417ad7b4 --- /dev/null +++ b/integration_tests/build/index.html @@ -0,0 +1,11 @@ + + + + + + Document + + +

Hello world

+ + diff --git a/robyn/__init__.py b/robyn/__init__.py index 8c623cf5f..ad5eb6013 100644 --- a/robyn/__init__.py +++ b/robyn/__init__.py @@ -4,6 +4,7 @@ from inspect import signature import multiprocessing as mp from robyn.events import Events +import logging # custom imports and exports from .robyn import SocketHeld @@ -19,8 +20,11 @@ from multiprocess import Process from watchdog.observers import Observer + mp.allow_connection_pickling() +logger = logging.getLogger(__name__) + class Robyn: """This is the python wrapper for the Robyn binaries.""" @@ -30,9 +34,10 @@ def __init__(self, file_object): self.file_path = file_object self.directory_path = directory_path self.parser = ArgumentParser() - self.dev = self.parser.is_dev() - self.processes = self.parser.num_processes() - self.workers = self.parser.workers() + self.dev = self.parser.is_dev + self.processes = self.parser.num_processes + self.workers = self.parser.workers + self.log_level = self.parser.log_level self.router = Router() self.middleware_router = MiddlewareRouter() self.web_socket_router = WebSocketRouter() @@ -40,6 +45,8 @@ def __init__(self, file_object): self.directories = [] self.event_handlers = {} + self._config_logger() + def _add_route(self, route_type, endpoint, handler): """ [This is base handler for all the decorators] @@ -83,7 +90,7 @@ def add_web_socket(self, endpoint, ws): self.web_socket_router.add_route(endpoint, ws) def _add_event_handler(self, event_type: str, handler): - print(f"Add event {event_type} handler") + logger.debug(f"Add event {event_type} handler") if event_type not in {Events.STARTUP, Events.SHUTDOWN}: return @@ -125,18 +132,19 @@ def start(self, url="127.0.0.1", port=5000): p.start() processes.append(p) - print("Press Ctrl + C to stop \n") + logger.info(f"{Colors.HEADER}Starting up \n{Colors.ENDC}") + logger.info(f"{Colors.OKGREEN}Press Ctrl + C to stop \n{Colors.ENDC}") try: for process in processes: process.join() except KeyboardInterrupt: - print(f"\n{Colors.BOLD}{Colors.OKGREEN} Terminating server!! {Colors.ENDC}") + logger.info(f"{Colors.BOLD}{Colors.OKGREEN} Terminating server!! {Colors.ENDC}") for process in processes: process.kill() else: event_handler = EventHandler(self.file_path) event_handler.start_server_first_time() - print( + logger.info( f"{Colors.OKBLUE}Dev server initialised with the directory_path : {self.directory_path}{Colors.ENDC}" ) observer = Observer() @@ -255,3 +263,17 @@ def inner(handler): self._add_route("TRACE", endpoint, handler) return inner + + def _config_logger(self): + """ + This is the method to configure the logger either on the dev mode or the env variable + """ + + log_level = "WARN" + + if self.dev: + log_level = "DEBUG" + + log_level = self.log_level if self.log_level else log_level + logging.basicConfig(level=log_level) + diff --git a/robyn/argument_parser.py b/robyn/argument_parser.py index fbfc89d64..e940c402e 100644 --- a/robyn/argument_parser.py +++ b/robyn/argument_parser.py @@ -28,14 +28,28 @@ def __init__(self): help="Development mode. It restarts the server based on file changes.", ) + self.parser.add_argument( + "--log-level", + dest="log_level", + default="INFO", + help="Set the log level name", + ) + self.args = self.parser.parse_args() + @property def num_processes(self): return self.args.processes + @property def workers(self): return self.args.workers + @property + def log_level(self): + return self.args.log_level + + @property def is_dev(self): _is_dev = self.args.dev if _is_dev and (self.num_processes() != 1 or self.workers() != 1): diff --git a/robyn/argument_parser.pyi b/robyn/argument_parser.pyi index 2125f1979..4ebbe4c81 100644 --- a/robyn/argument_parser.pyi +++ b/robyn/argument_parser.pyi @@ -1,7 +1,23 @@ import argparse + class ArgumentParser(argparse.ArgumentParser): - def __init__(self) -> None: ... - def num_processes(self): ... - def workers(self): ... - def is_dev(self): ... + def __init__(self) -> None: + ... + + @property + def num_processes(self) -> int: + ... + + @property + def workers(self) -> int: + ... + + @property + def is_dev(self) -> bool: + ... + + @property + def log_level(self) -> str: + ... + diff --git a/robyn/dev_event_handler.py b/robyn/dev_event_handler.py index 9524517f3..28a6523c7 100644 --- a/robyn/dev_event_handler.py +++ b/robyn/dev_event_handler.py @@ -1,11 +1,11 @@ # default imports import subprocess -# custom imports -from .log_colors import Colors - # third party imports from watchdog.events import FileSystemEventHandler +import logging + +logger = logging.getLogger(__name__) class EventHandler(FileSystemEventHandler): @@ -16,8 +16,6 @@ def __init__(self, file_name): def start_server_first_time(self): if self.processes: raise Exception("Something wrong with the server") - - print(f"{Colors.OKGREEN}Starting the server in dev mode{Colors.ENDC}") self.processes.append(subprocess.Popen(["python3", self.file_name], start_new_session=False)) def on_any_event(self, event): @@ -30,5 +28,4 @@ def on_any_event(self, event): if len(self.processes) > 0: for process in self.processes: process.terminate() - print(f"{Colors.OKGREEN}Starting the server in dev mode{Colors.ENDC}") self.processes.append(subprocess.Popen(["python3", self.file_name], start_new_session=False)) diff --git a/robyn/processpool.py b/robyn/processpool.py index a1330c42e..dfc69d9f0 100644 --- a/robyn/processpool.py +++ b/robyn/processpool.py @@ -4,8 +4,9 @@ import sys import multiprocessing as mp import asyncio +import logging -# import platform +logger = logging.getLogger(__name__) mp.allow_connection_pickling() @@ -67,7 +68,6 @@ def spawn_process( for endpoint in web_sockets: web_socket = web_sockets[endpoint] - print(web_socket.methods) server.add_web_socket_route( endpoint, web_socket.methods["connect"], diff --git a/robyn/router.py b/robyn/router.py index d80aae11d..bdd17daf5 100644 --- a/robyn/router.py +++ b/robyn/router.py @@ -19,7 +19,6 @@ def _format_response(self, res): response = {} if type(res) == dict: if "status_code" not in res: - print("Getting here") res["status_code"] = "200" response = res else: diff --git a/src/executors/mod.rs b/src/executors/mod.rs index baec61a85..587d40e48 100644 --- a/src/executors/mod.rs +++ b/src/executors/mod.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use actix_web::{http::Method, web, HttpRequest}; use anyhow::{bail, Result}; +use log::debug; // pyO3 module use crate::types::{Headers, PyFunction}; use futures_util::stream::StreamExt; @@ -187,7 +188,7 @@ pub async fn execute_http_function( let output = output.await?; let res = Python::with_gil(|py| -> PyResult> { - println!("This is the result of the code {:?}", output); + debug!("This is the result of the code {:?}", output); let mut res: HashMap = output.into_ref(py).downcast::()?.extract()?; @@ -238,7 +239,7 @@ pub async fn execute_event_handler( if let Some(handler) = event_handler { match &(*handler) { PyFunction::SyncFunction(function) => { - println!("Startup event handler"); + debug!("Startup event handler"); Python::with_gil(|py| -> Result<(), Box> { function.call0(py)?; Ok(()) @@ -246,7 +247,7 @@ pub async fn execute_event_handler( } PyFunction::CoRoutine(function) => { let future = Python::with_gil(|py| { - println!("Startup event handler async"); + debug!("Startup event handler async"); let coroutine = function.as_ref(py).call0().unwrap(); pyo3_asyncio::into_future_with_loop((*event_loop).as_ref(py), coroutine) diff --git a/src/request_handler/mod.rs b/src/request_handler/mod.rs index 1cbf39c21..471ee4707 100644 --- a/src/request_handler/mod.rs +++ b/src/request_handler/mod.rs @@ -1,5 +1,6 @@ use crate::executors::{execute_http_function, execute_middleware_function}; +use log::debug; use std::rc::Rc; use std::str::FromStr; use std::sync::Arc; @@ -49,7 +50,7 @@ pub async fn handle_http_request( { Ok(res) => res, Err(err) => { - println!("Error: {:?}", err); + debug!("Error: {:?}", err); let mut response = HttpResponse::InternalServerError(); apply_headers(&mut response, headers.clone()); return response.finish(); @@ -68,7 +69,7 @@ pub async fn handle_http_request( None => HashMap::new(), }; - println!("These are the headers from serde {:?}", headers); + debug!("These are the headers from serde {:?}", headers); let mut response = HttpResponse::build(status_code); apply_headers(&mut response, headers.clone()); @@ -78,7 +79,7 @@ pub async fn handle_http_request( response.finish() }; - println!( + debug!( "The status code is {} and the headers are {:?}", final_response.status(), final_response.headers() @@ -111,6 +112,6 @@ pub async fn handle_http_middleware_request( Err(_err) => HashMap::new(), }; - println!("These are the middleware response {:?}", contents); + debug!("These are the middleware response {:?}", contents); contents } diff --git a/src/routers/web_socket_router.rs b/src/routers/web_socket_router.rs index 4225d72fc..5b64def33 100644 --- a/src/routers/web_socket_router.rs +++ b/src/routers/web_socket_router.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::sync::RwLock; // pyo3 modules use crate::types::PyFunction; +use log::debug; use pyo3::prelude::*; use pyo3::types::PyAny; @@ -47,7 +48,7 @@ impl WebSocketRouter { PyFunction::SyncFunction(handler) }; - println!("socket type is {:?} {:?}", table, route); + debug!("socket type is {:?} {:?}", table, route); table .write() diff --git a/src/server.rs b/src/server.rs index 5c11eba30..ef8caabe3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -24,6 +24,7 @@ use actix_web::*; use dashmap::DashMap; // pyO3 module +use log::debug; use pyo3::prelude::*; static STARTED: AtomicBool = AtomicBool::new(false); @@ -68,11 +69,13 @@ impl Server { socket: &PyCell, workers: usize, ) -> PyResult<()> { + pyo3_log::init(); + if STARTED .compare_exchange(false, true, SeqCst, Relaxed) .is_err() { - println!("Already running..."); + debug!("Robyn is already running..."); return Ok(()); } @@ -103,7 +106,7 @@ impl Server { //init_current_thread_once(); let copied_event_loop = event_loop_hdl.clone(); actix_web::rt::System::new().block_on(async move { - println!("The number of workers are {}", workers.clone()); + debug!("The number of workers are {}", workers.clone()); execute_event_handler(startup_handler, copied_event_loop.clone()) .await .unwrap(); @@ -192,7 +195,7 @@ impl Server { let event_loop = (*event_loop).call_method0("run_forever"); if event_loop.is_err() { - println!("Ctrl c handler"); + debug!("Ctrl c handler"); Python::with_gil(|py| { let event_loop_hdl = event_loop_cleanup.clone(); pyo3_asyncio::tokio::run(py, async move { @@ -244,7 +247,7 @@ impl Server { is_async: bool, number_of_params: u8, ) { - println!("Route added for {} {} ", route_type, route); + debug!("Route added for {} {} ", route_type, route); self.router .add_route(route_type, route, handler, is_async, number_of_params) .unwrap(); @@ -260,7 +263,7 @@ impl Server { is_async: bool, number_of_params: u8, ) { - println!("MiddleWare Route added for {} {} ", route_type, route); + debug!("MiddleWare Route added for {} {} ", route_type, route); self.middleware_router .add_route(route_type, route, handler, is_async, number_of_params) .unwrap(); @@ -282,23 +285,23 @@ impl Server { /// Add a new startup handler pub fn add_startup_handler(&mut self, handler: Py, is_async: bool) { - println!("Adding startup handler"); + debug!("Adding startup handler"); match is_async { true => self.startup_handler = Some(Arc::new(PyFunction::CoRoutine(handler))), false => self.startup_handler = Some(Arc::new(PyFunction::SyncFunction(handler))), }; - println!("{:?}", self.startup_handler); + debug!("{:?}", self.startup_handler); } /// Add a new shutdown handler pub fn add_shutdown_handler(&mut self, handler: Py, is_async: bool) { - println!("Adding shutdown handler"); + debug!("Adding shutdown handler"); match is_async { true => self.shutdown_handler = Some(Arc::new(PyFunction::CoRoutine(handler))), false => self.shutdown_handler = Some(Arc::new(PyFunction::SyncFunction(handler))), }; - println!("{:?}", self.startup_handler); - println!("{:?}", self.shutdown_handler); + debug!("{:?}", self.startup_handler); + debug!("{:?}", self.shutdown_handler); } } @@ -341,13 +344,13 @@ async fn index( queries.clone(), ) .await; - println!("Middleware contents {:?}", x); + debug!("Middleware contents {:?}", x); x } None => HashMap::new(), }; - println!("These are the tuple params {:?}", tuple_params); + debug!("These are the tuple params {:?}", tuple_params); let mut headers_dup = HashMap::new(); @@ -387,7 +390,7 @@ async fn index( queries.clone(), ) .await; - println!("{:?}", x); + debug!("{:?}", x); } None => {} }; diff --git a/src/shared_socket.rs b/src/shared_socket.rs index 98c94e920..e40af77db 100644 --- a/src/shared_socket.rs +++ b/src/shared_socket.rs @@ -1,5 +1,6 @@ use pyo3::prelude::*; +use log::debug; use socket2::{Domain, Protocol, Socket, Type}; use std::net::SocketAddr; @@ -16,7 +17,7 @@ impl SocketHeld { pub fn new(address: String, port: i32) -> PyResult { let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?; let address: SocketAddr = format!("{}:{}", address, port).parse()?; - println!("{}", address); + debug!("{}", address); socket.set_reuse_port(true)?; socket.set_reuse_address(true)?; socket.bind(&address.into())?; @@ -30,7 +31,7 @@ impl SocketHeld { pub fn new(address: String, port: i32) -> PyResult { let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?; let address: SocketAddr = format!("{}:{}", address, port).parse()?; - println!("{}", address); + debug!("{}", address); // reuse port is not available on windows socket.set_reuse_address(true)?; socket.bind(&address.into())?; diff --git a/src/web_socket_connection.rs b/src/web_socket_connection.rs index af52c4c1d..cd939839b 100644 --- a/src/web_socket_connection.rs +++ b/src/web_socket_connection.rs @@ -5,6 +5,7 @@ use actix::{Actor, AsyncContext, StreamHandler}; use actix_web::{web, Error, HttpRequest, HttpResponse}; use actix_web_actors::ws; use actix_web_actors::ws::WebsocketContext; +use log::debug; use pyo3::prelude::*; use uuid::Uuid; @@ -85,7 +86,7 @@ impl Actor for MyWs { self, ); - println!("Actor is alive"); + debug!("Actor is alive"); } fn stopped(&mut self, ctx: &mut WebsocketContext) { @@ -99,7 +100,7 @@ impl Actor for MyWs { self, ); - println!("Actor is dead"); + debug!("Actor is dead"); } } @@ -112,10 +113,10 @@ impl StreamHandler> for MyWs { fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { match msg { Ok(ws::Message::Ping(msg)) => { - println!("Ping message {:?}", msg); + debug!("Ping message {:?}", msg); let handler_function = &self.router.get("connect").unwrap().0; let number_of_params = &self.router.get("connect").unwrap().1; - println!("{:?}", handler_function); + debug!("{:?}", handler_function); execute_ws_function( handler_function, *number_of_params, @@ -127,7 +128,7 @@ impl StreamHandler> for MyWs { } Ok(ws::Message::Pong(msg)) => { - println!("Pong message {:?}", msg); + debug!("Pong message {:?}", msg); ctx.pong(&msg) } @@ -146,7 +147,7 @@ impl StreamHandler> for MyWs { Ok(ws::Message::Binary(bin)) => ctx.binary(bin), Ok(ws::Message::Close(_close_reason)) => { - println!("Socket was closed"); + debug!("Socket was closed"); let handler_function = &self.router.get("close").expect("No close function").0; let number_of_params = &self.router.get("close").unwrap().1; execute_ws_function(