@@ -695,6 +695,11 @@ class Application(object):
695695 log_level : str, optional
696696 Logging level for the application logger. Valid values are 'info',
697697 'debug', 'warning', 'error'. Defaults to 'info'.
698+ disable_metrics : bool, optional
699+ Disable logging of function call metrics. Defaults to False.
700+ name : str, optional
701+ Name for the application instance. Used to create a logger-specific
702+ name. If not provided, a random name will be generated.
698703
699704 """
700705
@@ -864,6 +869,8 @@ def __init__(
864869 log_file : Optional [str ] = get_option ('external_function.log_file' ),
865870 log_format : str = get_option ('external_function.log_format' ),
866871 log_level : str = get_option ('external_function.log_level' ),
872+ disable_metrics : bool = get_option ('external_function.disable_metrics' ),
873+ name : Optional [str ] = None ,
867874 ) -> None :
868875 if link_name and (link_config or link_credentials ):
869876 raise ValueError (
@@ -880,6 +887,15 @@ def __init__(
880887 get_option ('external_function.link_credentials' ) or '{}' ,
881888 ) or None
882889
890+ # Generate application name if not provided
891+ if name is None :
892+ name = f'ext_app_{ secrets .token_hex (4 )} '
893+
894+ self .name = name
895+
896+ # Create logger instance specific to this application
897+ self .logger = utils .get_logger (f'singlestoredb.functions.ext.asgi.{ self .name } ' )
898+
883899 # List of functions specs
884900 specs : List [Union [str , Callable [..., Any ], ModuleType ]] = []
885901
@@ -974,19 +990,20 @@ def __init__(
974990 self .log_file = log_file
975991 self .log_format = log_format
976992 self .log_level = log_level
993+ self .disable_metrics = disable_metrics
977994
978995 # Configure logging
979996 self ._configure_logging ()
980997
981998 def _configure_logging (self ) -> None :
982999 """Configure logging based on the log_file and log_format settings."""
9831000 # Set logger level
984- logger .setLevel (getattr (logging , self .log_level .upper ()))
1001+ self . logger .setLevel (getattr (logging , self .log_level .upper ()))
9851002
9861003 # Configure log file if specified
9871004 if self .log_file :
9881005 # Remove existing handlers
989- logger .handlers .clear ()
1006+ self . logger .handlers .clear ()
9901007
9911008 # Create file handler
9921009 file_handler = logging .FileHandler (self .log_file )
@@ -997,7 +1014,7 @@ def _configure_logging(self) -> None:
9971014 file_handler .setFormatter (formatter )
9981015
9991016 # Add the handler to the logger
1000- logger .addHandler (file_handler )
1017+ self . logger .addHandler (file_handler )
10011018
10021019 async def __call__ (
10031020 self ,
@@ -1059,7 +1076,7 @@ async def __call__(
10591076 # Call the endpoint
10601077 if method == 'POST' and func is not None and path == self .invoke_path :
10611078
1062- logger .info (
1079+ self . logger .info (
10631080 json .dumps ({
10641081 'type' : 'function_call' ,
10651082 'id' : request_id ,
@@ -1146,7 +1163,7 @@ async def __call__(
11461163 await send (output_handler ['response' ])
11471164
11481165 except asyncio .TimeoutError :
1149- logger .exception (
1166+ self . logger .exception (
11501167 'Timeout in function call: ' + func_name .decode ('utf-8' ),
11511168 )
11521169 body = (
@@ -1157,14 +1174,14 @@ async def __call__(
11571174 await send (self .error_response_dict )
11581175
11591176 except asyncio .CancelledError :
1160- logger .exception (
1177+ self . logger .exception (
11611178 'Function call cancelled: ' + func_name .decode ('utf-8' ),
11621179 )
11631180 body = b'[CancelledError] Function call was cancelled'
11641181 await send (self .error_response_dict )
11651182
11661183 except Exception as e :
1167- logger .exception (
1184+ self . logger .exception (
11681185 'Error in function call: ' + func_name .decode ('utf-8' ),
11691186 )
11701187 body = f'[{ type (e ).__name__ } ] { str (e ).strip ()} ' .encode ('utf-8' )
@@ -1218,7 +1235,8 @@ async def __call__(
12181235 for k , v in call_timer .metrics .items ():
12191236 timer .metrics [k ] = v
12201237
1221- logger .info (json .dumps (timer .finish ()))
1238+ if not self .disable_metrics :
1239+ self .logger .info (json .dumps (timer .finish ()))
12221240
12231241 def _create_link (
12241242 self ,
@@ -1329,8 +1347,9 @@ def get_function_info(
13291347 doc_examples .append (ex_dict )
13301348
13311349 except Exception as e :
1332- logger .warning (
1333- f'Could not parse docstring for function { key } : { e } ' ,
1350+ self .logger .warning (
1351+ f'Could not parse docstring for function '
1352+ f'{ key .decode ("utf-8" )} : { e } ' ,
13341353 )
13351354
13361355 if not func_name or key == func_name :
@@ -1801,6 +1820,14 @@ def main(argv: Optional[List[str]] = None) -> None:
18011820 ),
18021821 help = 'Log format string for formatting log messages' ,
18031822 )
1823+ parser .add_argument (
1824+ '--disable-metrics' , action = 'store_true' ,
1825+ default = defaults .get (
1826+ 'disable_metrics' ,
1827+ get_option ('external_function.disable_metrics' ),
1828+ ),
1829+ help = 'Disable logging of function call metrics' ,
1830+ )
18041831 parser .add_argument (
18051832 '--name-prefix' , metavar = 'name_prefix' ,
18061833 default = defaults .get (
@@ -1926,6 +1953,7 @@ def main(argv: Optional[List[str]] = None) -> None:
19261953 log_file = args .log_file ,
19271954 log_format = args .log_format ,
19281955 log_level = args .log_level ,
1956+ disable_metrics = args .disable_metrics ,
19291957 )
19301958
19311959 funcs = app .get_create_functions (replace = args .replace_existing )
0 commit comments