Skip to content

Commit 0cd8d51

Browse files
committed
Add disable_metrics option to external functions
- Add external_function.disable_metrics configuration option (default: False) - Add disable_metrics parameter to Application class constructor - Update Application to create logger-specific instances with unique names - Add --disable-metrics command-line argument to main function - Conditionally log metrics based on disable_metrics flag - Support configuration via environment variable SINGLESTOREDB_EXT_FUNC_DISABLE_METRICS This allows users to disable the logging of function call metrics when needed, while maintaining backward compatibility with existing behavior.
1 parent ad74c9e commit 0cd8d51

File tree

2 files changed

+44
-10
lines changed

2 files changed

+44
-10
lines changed

singlestoredb/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,12 @@
462462
environ=['SINGLESTOREDB_EXT_FUNC_TIMEOUT'],
463463
)
464464

465+
register_option(
466+
'external_function.disable_metrics', 'bool', check_bool, False,
467+
'Disable logging of function call metrics.',
468+
environ=['SINGLESTOREDB_EXT_FUNC_DISABLE_METRICS'],
469+
)
470+
465471
#
466472
# Debugging options
467473
#

singlestoredb/functions/ext/asgi.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)