Logging Level Update (#2165)

This commit is contained in:
Yuhong Sun 2024-08-18 21:53:40 -07:00 committed by GitHub
parent 119aefba88
commit 5ab4d94d94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 183 additions and 201 deletions

View File

@ -81,7 +81,7 @@ def verify_auth_setting() -> None:
"User must choose a valid user authentication method: " "User must choose a valid user authentication method: "
"disabled, basic, or google_oauth" "disabled, basic, or google_oauth"
) )
logger.info(f"Using Auth Type: {AUTH_TYPE.value}") logger.notice(f"Using Auth Type: {AUTH_TYPE.value}")
def get_display_email(email: str | None, space_less: bool = False) -> str: def get_display_email(email: str | None, space_less: bool = False) -> str:
@ -214,7 +214,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
async def on_after_register( async def on_after_register(
self, user: User, request: Optional[Request] = None self, user: User, request: Optional[Request] = None
) -> None: ) -> None:
logger.info(f"User {user.id} has registered.") logger.notice(f"User {user.id} has registered.")
optional_telemetry( optional_telemetry(
record_type=RecordType.SIGN_UP, record_type=RecordType.SIGN_UP,
data={"action": "create"}, data={"action": "create"},
@ -224,14 +224,14 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
async def on_after_forgot_password( async def on_after_forgot_password(
self, user: User, token: str, request: Optional[Request] = None self, user: User, token: str, request: Optional[Request] = None
) -> None: ) -> None:
logger.info(f"User {user.id} has forgot their password. Reset token: {token}") logger.notice(f"User {user.id} has forgot their password. Reset token: {token}")
async def on_after_request_verify( async def on_after_request_verify(
self, user: User, token: str, request: Optional[Request] = None self, user: User, token: str, request: Optional[Request] = None
) -> None: ) -> None:
verify_email_domain(user.email) verify_email_domain(user.email)
logger.info( logger.notice(
f"Verification requested for user {user.id}. Verification token: {token}" f"Verification requested for user {user.id}. Verification token: {token}"
) )

View File

@ -282,7 +282,7 @@ def check_for_cc_pair_deletion_task() -> None:
cc_pairs = get_connector_credential_pairs(db_session) cc_pairs = get_connector_credential_pairs(db_session)
for cc_pair in cc_pairs: for cc_pair in cc_pairs:
if should_kick_off_deletion_of_cc_pair(cc_pair, db_session): if should_kick_off_deletion_of_cc_pair(cc_pair, db_session):
logger.info(f"Deleting the {cc_pair.name} connector credential pair") logger.notice(f"Deleting the {cc_pair.name} connector credential pair")
cleanup_connector_credential_pair_task.apply_async( cleanup_connector_credential_pair_task.apply_async(
kwargs=dict( kwargs=dict(
connector_id=cc_pair.connector.id, connector_id=cc_pair.connector.id,

View File

@ -92,7 +92,7 @@ def should_sync_doc_set(document_set: DocumentSet, db_session: Session) -> bool:
logger.info(f"Document set '{document_set.id}' is already syncing. Skipping.") logger.info(f"Document set '{document_set.id}' is already syncing. Skipping.")
return False return False
logger.info(f"Document set {document_set.id} syncing now!") logger.info(f"Document set {document_set.id} syncing now.")
return True return True

View File

@ -185,11 +185,11 @@ def delete_connector_credential_pair(
connector_id=connector_id, connector_id=connector_id,
) )
if not connector or not len(connector.credentials): if not connector or not len(connector.credentials):
logger.debug("Found no credentials left for connector, deleting connector") logger.info("Found no credentials left for connector, deleting connector")
db_session.delete(connector) db_session.delete(connector)
db_session.commit() db_session.commit()
logger.info( logger.notice(
"Successfully deleted connector_credential_pair with connector_id:" "Successfully deleted connector_credential_pair with connector_id:"
f" '{connector_id}' and credential_id: '{credential_id}'. Deleted {num_docs_deleted} docs." f" '{connector_id}' and credential_id: '{credential_id}'. Deleted {num_docs_deleted} docs."
) )

View File

@ -160,7 +160,7 @@ def _run_indexing(
) )
if INDEXING_TRACER_INTERVAL > 0: if INDEXING_TRACER_INTERVAL > 0:
logger.info(f"Memory tracer starting: interval={INDEXING_TRACER_INTERVAL}") logger.debug(f"Memory tracer starting: interval={INDEXING_TRACER_INTERVAL}")
tracer = DanswerTracer() tracer = DanswerTracer()
tracer.start() tracer.start()
tracer.snap() tracer.snap()
@ -269,7 +269,7 @@ def _run_indexing(
INDEXING_TRACER_INTERVAL > 0 INDEXING_TRACER_INTERVAL > 0
and tracer_counter % INDEXING_TRACER_INTERVAL == 0 and tracer_counter % INDEXING_TRACER_INTERVAL == 0
): ):
logger.info( logger.debug(
f"Running trace comparison for batch {tracer_counter}. interval={INDEXING_TRACER_INTERVAL}" f"Running trace comparison for batch {tracer_counter}. interval={INDEXING_TRACER_INTERVAL}"
) )
tracer.snap() tracer.snap()
@ -285,7 +285,7 @@ def _run_indexing(
run_dt=run_end_dt, run_dt=run_end_dt,
) )
except Exception as e: except Exception as e:
logger.info( logger.exception(
f"Connector run ran into exception after elapsed time: {time.time() - start_time} seconds" f"Connector run ran into exception after elapsed time: {time.time() - start_time} seconds"
) )
# Only mark the attempt as a complete failure if this is the first indexing window. # Only mark the attempt as a complete failure if this is the first indexing window.
@ -323,13 +323,13 @@ def _run_indexing(
break break
if INDEXING_TRACER_INTERVAL > 0: if INDEXING_TRACER_INTERVAL > 0:
logger.info( logger.debug(
f"Running trace comparison between start and end of indexing. {tracer_counter} batches processed." f"Running trace comparison between start and end of indexing. {tracer_counter} batches processed."
) )
tracer.snap() tracer.snap()
tracer.log_first_diff(INDEXING_TRACER_NUM_PRINT_ENTRIES) tracer.log_first_diff(INDEXING_TRACER_NUM_PRINT_ENTRIES)
tracer.stop() tracer.stop()
logger.info("Memory tracer stopped.") logger.debug("Memory tracer stopped.")
if ( if (
index_attempt_md.num_exceptions > 0 index_attempt_md.num_exceptions > 0

View File

@ -48,9 +48,9 @@ class DanswerTracer:
stats = self.snapshot.statistics("traceback") stats = self.snapshot.statistics("traceback")
for s in stats[:numEntries]: for s in stats[:numEntries]:
logger.info(f"Tracer snap: {s}") logger.debug(f"Tracer snap: {s}")
for line in s.traceback: for line in s.traceback:
logger.info(f"* {line}") logger.debug(f"* {line}")
@staticmethod @staticmethod
def log_diff( def log_diff(
@ -60,9 +60,9 @@ class DanswerTracer:
) -> None: ) -> None:
stats = snap_current.compare_to(snap_previous, "traceback") stats = snap_current.compare_to(snap_previous, "traceback")
for s in stats[:numEntries]: for s in stats[:numEntries]:
logger.info(f"Tracer diff: {s}") logger.debug(f"Tracer diff: {s}")
for line in s.traceback.format(): for line in s.traceback.format():
logger.info(f"* {line}") logger.debug(f"* {line}")
def log_previous_diff(self, numEntries: int) -> None: def log_previous_diff(self, numEntries: int) -> None:
if not self.snapshot or not self.snapshot_prev: if not self.snapshot or not self.snapshot_prev:

View File

@ -380,7 +380,7 @@ def update_loop(
# batch of documents indexed # batch of documents indexed
if db_embedding_model.cloud_provider_id is None: if db_embedding_model.cloud_provider_id is None:
logger.debug("Running a first inference to warm up embedding model") logger.notice("Running a first inference to warm up embedding model")
warm_up_bi_encoder( warm_up_bi_encoder(
embedding_model=db_embedding_model, embedding_model=db_embedding_model,
model_server_host=INDEXING_MODEL_SERVER_HOST, model_server_host=INDEXING_MODEL_SERVER_HOST,
@ -447,7 +447,7 @@ def update__main() -> None:
set_is_ee_based_on_env_variable() set_is_ee_based_on_env_variable()
init_sqlalchemy_engine(POSTGRES_INDEXER_APP_NAME) init_sqlalchemy_engine(POSTGRES_INDEXER_APP_NAME)
logger.info("Starting indexing service") logger.notice("Starting indexing service")
update_loop() update_loop()

View File

@ -13,6 +13,10 @@ DEFAULT_BOOST = 0
SESSION_KEY = "session" SESSION_KEY = "session"
# Used for logging
SLACK_CHANNEL_ID = "channel_id"
# For chunking/processing chunks # For chunking/processing chunks
RETURN_SEPARATOR = "\n\r\n" RETURN_SEPARATOR = "\n\r\n"
SECTION_SEPARATOR = "\n\n" SECTION_SEPARATOR = "\n\n"

View File

@ -56,7 +56,7 @@ class BlobStorageConnector(LoadConnector, PollConnector):
Raises ValueError for unsupported bucket types. Raises ValueError for unsupported bucket types.
""" """
logger.info( logger.debug(
f"Loading credentials for {self.bucket_name} or type {self.bucket_type}" f"Loading credentials for {self.bucket_name} or type {self.bucket_type}"
) )
@ -220,7 +220,7 @@ class BlobStorageConnector(LoadConnector, PollConnector):
yield batch yield batch
def load_from_state(self) -> GenerateDocumentsOutput: def load_from_state(self) -> GenerateDocumentsOutput:
logger.info("Loading blob objects") logger.debug("Loading blob objects")
return self._yield_blob_objects( return self._yield_blob_objects(
start=datetime(1970, 1, 1, tzinfo=timezone.utc), start=datetime(1970, 1, 1, tzinfo=timezone.utc),
end=datetime.now(timezone.utc), end=datetime.now(timezone.utc),

View File

@ -56,7 +56,7 @@ class _RateLimitDecorator:
sleep_cnt = 0 sleep_cnt = 0
while len(self.call_history) == self.max_calls: while len(self.call_history) == self.max_calls:
sleep_time = self.sleep_time * (self.sleep_backoff**sleep_cnt) sleep_time = self.sleep_time * (self.sleep_backoff**sleep_cnt)
logger.info( logger.notice(
f"Rate limit exceeded for function {func.__name__}. " f"Rate limit exceeded for function {func.__name__}. "
f"Waiting {sleep_time} seconds before retrying." f"Waiting {sleep_time} seconds before retrying."
) )

View File

@ -38,7 +38,7 @@ def _sleep_after_rate_limit_exception(github_client: Github) -> None:
tzinfo=timezone.utc tzinfo=timezone.utc
) - datetime.now(tz=timezone.utc) ) - datetime.now(tz=timezone.utc)
sleep_time += timedelta(minutes=1) # add an extra minute just to be safe sleep_time += timedelta(minutes=1) # add an extra minute just to be safe
logger.info(f"Ran into Github rate-limit. Sleeping {sleep_time.seconds} seconds.") logger.notice(f"Ran into Github rate-limit. Sleeping {sleep_time.seconds} seconds.")
time.sleep(sleep_time.seconds) time.sleep(sleep_time.seconds)

View File

@ -50,7 +50,7 @@ def get_gmail_creds_for_authorized_user(
try: try:
creds.refresh(Request()) creds.refresh(Request())
if creds.valid: if creds.valid:
logger.info("Refreshed Gmail tokens.") logger.notice("Refreshed Gmail tokens.")
return creds return creds
except Exception as e: except Exception as e:
logger.exception(f"Failed to refresh gmail access token due to: {e}") logger.exception(f"Failed to refresh gmail access token due to: {e}")

View File

@ -81,10 +81,10 @@ class GongConnector(LoadConnector, PollConnector):
for workspace in workspace_list: for workspace in workspace_list:
if workspace: if workspace:
logger.info(f"Updating workspace: {workspace}") logger.info(f"Updating Gong workspace: {workspace}")
workspace_id = workspace_map.get(workspace) workspace_id = workspace_map.get(workspace)
if not workspace_id: if not workspace_id:
logger.error(f"Invalid workspace: {workspace}") logger.error(f"Invalid Gong workspace: {workspace}")
if not self.continue_on_fail: if not self.continue_on_fail:
raise ValueError(f"Invalid workspace: {workspace}") raise ValueError(f"Invalid workspace: {workspace}")
continue continue

View File

@ -267,7 +267,7 @@ def get_all_files_batched(
yield from batch_generator( yield from batch_generator(
items=found_files, items=found_files,
batch_size=batch_size, batch_size=batch_size,
pre_batch_yield=lambda batch_files: logger.info( pre_batch_yield=lambda batch_files: logger.debug(
f"Parseable Documents in batch: {[file['name'] for file in batch_files]}" f"Parseable Documents in batch: {[file['name'] for file in batch_files]}"
), ),
) )

View File

@ -50,7 +50,7 @@ def get_google_drive_creds_for_authorized_user(
try: try:
creds.refresh(Request()) creds.refresh(Request())
if creds.valid: if creds.valid:
logger.info("Refreshed Google Drive tokens.") logger.notice("Refreshed Google Drive tokens.")
return creds return creds
except Exception as e: except Exception as e:
logger.exception(f"Failed to refresh google drive access token due to: {e}") logger.exception(f"Failed to refresh google drive access token due to: {e}")

View File

@ -6,7 +6,6 @@ FEEDBACK_DOC_BUTTON_BLOCK_ACTION_ID = "feedback-doc-button"
IMMEDIATE_RESOLVED_BUTTON_ACTION_ID = "immediate-resolved-button" IMMEDIATE_RESOLVED_BUTTON_ACTION_ID = "immediate-resolved-button"
FOLLOWUP_BUTTON_ACTION_ID = "followup-button" FOLLOWUP_BUTTON_ACTION_ID = "followup-button"
FOLLOWUP_BUTTON_RESOLVED_ACTION_ID = "followup-resolved-button" FOLLOWUP_BUTTON_RESOLVED_ACTION_ID = "followup-resolved-button"
SLACK_CHANNEL_ID = "channel_id"
VIEW_DOC_FEEDBACK_ID = "view-doc-feedback" VIEW_DOC_FEEDBACK_ID = "view-doc-feedback"
GENERATE_ANSWER_BUTTON_ACTION_ID = "generate-answer-button" GENERATE_ANSWER_BUTTON_ACTION_ID = "generate-answer-button"

View File

@ -1,4 +1,3 @@
import logging
from typing import Any from typing import Any
from typing import cast from typing import cast
@ -134,7 +133,7 @@ def handle_generate_answer_button(
receiver_ids=None, receiver_ids=None,
client=client.web_client, client=client.web_client,
channel=channel_id, channel=channel_id,
logger=cast(logging.Logger, logger), logger=logger,
feedback_reminder_id=None, feedback_reminder_id=None,
) )

View File

@ -1,15 +1,13 @@
import datetime import datetime
import logging
from typing import cast
from slack_sdk import WebClient from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError from slack_sdk.errors import SlackApiError
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from danswer.configs.constants import SLACK_CHANNEL_ID
from danswer.configs.danswerbot_configs import DANSWER_BOT_FEEDBACK_REMINDER from danswer.configs.danswerbot_configs import DANSWER_BOT_FEEDBACK_REMINDER
from danswer.configs.danswerbot_configs import DANSWER_REACT_EMOJI from danswer.configs.danswerbot_configs import DANSWER_REACT_EMOJI
from danswer.danswerbot.slack.blocks import get_feedback_reminder_blocks from danswer.danswerbot.slack.blocks import get_feedback_reminder_blocks
from danswer.danswerbot.slack.constants import SLACK_CHANNEL_ID
from danswer.danswerbot.slack.handlers.handle_regular_answer import ( from danswer.danswerbot.slack.handlers.handle_regular_answer import (
handle_regular_answer, handle_regular_answer,
) )
@ -17,7 +15,6 @@ from danswer.danswerbot.slack.handlers.handle_standard_answers import (
handle_standard_answers, handle_standard_answers,
) )
from danswer.danswerbot.slack.models import SlackMessageInfo from danswer.danswerbot.slack.models import SlackMessageInfo
from danswer.danswerbot.slack.utils import ChannelIdAdapter
from danswer.danswerbot.slack.utils import fetch_user_ids_from_emails from danswer.danswerbot.slack.utils import fetch_user_ids_from_emails
from danswer.danswerbot.slack.utils import fetch_user_ids_from_groups from danswer.danswerbot.slack.utils import fetch_user_ids_from_groups
from danswer.danswerbot.slack.utils import respond_in_thread from danswer.danswerbot.slack.utils import respond_in_thread
@ -53,12 +50,8 @@ def send_msg_ack_to_user(details: SlackMessageInfo, client: WebClient) -> None:
def schedule_feedback_reminder( def schedule_feedback_reminder(
details: SlackMessageInfo, include_followup: bool, client: WebClient details: SlackMessageInfo, include_followup: bool, client: WebClient
) -> str | None: ) -> str | None:
logger = cast( logger = setup_logger(extra={SLACK_CHANNEL_ID: details.channel_to_respond})
logging.Logger,
ChannelIdAdapter(
logger_base, extra={SLACK_CHANNEL_ID: details.channel_to_respond}
),
)
if not DANSWER_BOT_FEEDBACK_REMINDER: if not DANSWER_BOT_FEEDBACK_REMINDER:
logger.info("Scheduled feedback reminder disabled...") logger.info("Scheduled feedback reminder disabled...")
return None return None
@ -97,10 +90,7 @@ def schedule_feedback_reminder(
def remove_scheduled_feedback_reminder( def remove_scheduled_feedback_reminder(
client: WebClient, channel: str | None, msg_id: str client: WebClient, channel: str | None, msg_id: str
) -> None: ) -> None:
logger = cast( logger = setup_logger(extra={SLACK_CHANNEL_ID: channel})
logging.Logger,
ChannelIdAdapter(logger_base, extra={SLACK_CHANNEL_ID: channel}),
)
try: try:
client.chat_deleteScheduledMessage( client.chat_deleteScheduledMessage(
@ -129,10 +119,7 @@ def handle_message(
""" """
channel = message_info.channel_to_respond channel = message_info.channel_to_respond
logger = cast( logger = setup_logger(extra={SLACK_CHANNEL_ID: channel})
logging.Logger,
ChannelIdAdapter(logger_base, extra={SLACK_CHANNEL_ID: channel}),
)
messages = message_info.thread_messages messages = message_info.thread_messages
sender_id = message_info.sender sender_id = message_info.sender

View File

@ -1,5 +1,4 @@
import functools import functools
import logging
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from typing import cast from typing import cast
@ -51,6 +50,7 @@ from danswer.search.enums import OptionalSearchSetting
from danswer.search.models import BaseFilters from danswer.search.models import BaseFilters
from danswer.search.models import RetrievalDetails from danswer.search.models import RetrievalDetails
from danswer.search.search_settings import get_search_settings from danswer.search.search_settings import get_search_settings
from danswer.utils.logger import DanswerLoggingAdapter
srl = SlackRateLimiter() srl = SlackRateLimiter()
@ -83,7 +83,7 @@ def handle_regular_answer(
receiver_ids: list[str] | None, receiver_ids: list[str] | None,
client: WebClient, client: WebClient,
channel: str, channel: str,
logger: logging.Logger, logger: DanswerLoggingAdapter,
feedback_reminder_id: str | None, feedback_reminder_id: str | None,
num_retries: int = DANSWER_BOT_NUM_RETRIES, num_retries: int = DANSWER_BOT_NUM_RETRIES,
answer_generation_timeout: int = DANSWER_BOT_ANSWER_GENERATION_TIMEOUT, answer_generation_timeout: int = DANSWER_BOT_ANSWER_GENERATION_TIMEOUT,
@ -136,7 +136,6 @@ def handle_regular_answer(
tries=num_retries, tries=num_retries,
delay=0.25, delay=0.25,
backoff=2, backoff=2,
logger=logger,
) )
@rate_limits(client=client, channel=channel, thread_ts=message_ts_to_respond_to) @rate_limits(client=client, channel=channel, thread_ts=message_ts_to_respond_to)
def _get_answer(new_message_request: DirectQARequest) -> OneShotQAResponse | None: def _get_answer(new_message_request: DirectQARequest) -> OneShotQAResponse | None:
@ -319,7 +318,7 @@ def handle_regular_answer(
) )
if answer.answer_valid is False: if answer.answer_valid is False:
logger.info( logger.notice(
"Answer was evaluated to be invalid, throwing it away without responding." "Answer was evaluated to be invalid, throwing it away without responding."
) )
update_emote_react( update_emote_react(
@ -357,7 +356,7 @@ def handle_regular_answer(
return True return True
if not answer.answer and disable_docs_only_answer: if not answer.answer and disable_docs_only_answer:
logger.info( logger.notice(
"Unable to find answer - not responding since the " "Unable to find answer - not responding since the "
"`DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER` env variable is set" "`DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER` env variable is set"
) )

View File

@ -1,5 +1,3 @@
import logging
from slack_sdk import WebClient from slack_sdk import WebClient
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@ -21,6 +19,7 @@ from danswer.db.models import SlackBotConfig
from danswer.db.standard_answer import fetch_standard_answer_categories_by_names from danswer.db.standard_answer import fetch_standard_answer_categories_by_names
from danswer.db.standard_answer import find_matching_standard_answers from danswer.db.standard_answer import find_matching_standard_answers
from danswer.server.manage.models import StandardAnswer from danswer.server.manage.models import StandardAnswer
from danswer.utils.logger import DanswerLoggingAdapter
from danswer.utils.logger import setup_logger from danswer.utils.logger import setup_logger
logger = setup_logger() logger = setup_logger()
@ -61,7 +60,7 @@ def handle_standard_answers(
receiver_ids: list[str] | None, receiver_ids: list[str] | None,
slack_bot_config: SlackBotConfig | None, slack_bot_config: SlackBotConfig | None,
prompt: Prompt | None, prompt: Prompt | None,
logger: logging.Logger, logger: DanswerLoggingAdapter,
client: WebClient, client: WebClient,
db_session: Session, db_session: Session,
) -> bool: ) -> bool:

View File

@ -10,6 +10,7 @@ from slack_sdk.socket_mode.response import SocketModeResponse
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from danswer.configs.constants import MessageType from danswer.configs.constants import MessageType
from danswer.configs.constants import SLACK_CHANNEL_ID
from danswer.configs.danswerbot_configs import DANSWER_BOT_REPHRASE_MESSAGE from danswer.configs.danswerbot_configs import DANSWER_BOT_REPHRASE_MESSAGE
from danswer.configs.danswerbot_configs import DANSWER_BOT_RESPOND_EVERY_CHANNEL from danswer.configs.danswerbot_configs import DANSWER_BOT_RESPOND_EVERY_CHANNEL
from danswer.configs.danswerbot_configs import NOTIFY_SLACKBOT_NO_ANSWER from danswer.configs.danswerbot_configs import NOTIFY_SLACKBOT_NO_ANSWER
@ -21,7 +22,6 @@ from danswer.danswerbot.slack.constants import FOLLOWUP_BUTTON_RESOLVED_ACTION_I
from danswer.danswerbot.slack.constants import GENERATE_ANSWER_BUTTON_ACTION_ID from danswer.danswerbot.slack.constants import GENERATE_ANSWER_BUTTON_ACTION_ID
from danswer.danswerbot.slack.constants import IMMEDIATE_RESOLVED_BUTTON_ACTION_ID from danswer.danswerbot.slack.constants import IMMEDIATE_RESOLVED_BUTTON_ACTION_ID
from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID
from danswer.danswerbot.slack.constants import SLACK_CHANNEL_ID
from danswer.danswerbot.slack.constants import VIEW_DOC_FEEDBACK_ID from danswer.danswerbot.slack.constants import VIEW_DOC_FEEDBACK_ID
from danswer.danswerbot.slack.handlers.handle_buttons import handle_doc_feedback_button from danswer.danswerbot.slack.handlers.handle_buttons import handle_doc_feedback_button
from danswer.danswerbot.slack.handlers.handle_buttons import handle_followup_button from danswer.danswerbot.slack.handlers.handle_buttons import handle_followup_button
@ -39,7 +39,6 @@ from danswer.danswerbot.slack.handlers.handle_message import (
from danswer.danswerbot.slack.handlers.handle_message import schedule_feedback_reminder from danswer.danswerbot.slack.handlers.handle_message import schedule_feedback_reminder
from danswer.danswerbot.slack.models import SlackMessageInfo from danswer.danswerbot.slack.models import SlackMessageInfo
from danswer.danswerbot.slack.tokens import fetch_tokens from danswer.danswerbot.slack.tokens import fetch_tokens
from danswer.danswerbot.slack.utils import ChannelIdAdapter
from danswer.danswerbot.slack.utils import decompose_action_id from danswer.danswerbot.slack.utils import decompose_action_id
from danswer.danswerbot.slack.utils import get_channel_name_from_id from danswer.danswerbot.slack.utils import get_channel_name_from_id
from danswer.danswerbot.slack.utils import get_danswer_bot_app_id from danswer.danswerbot.slack.utils import get_danswer_bot_app_id
@ -84,18 +83,18 @@ def prefilter_requests(req: SocketModeRequest, client: SocketModeClient) -> bool
event = cast(dict[str, Any], req.payload.get("event", {})) event = cast(dict[str, Any], req.payload.get("event", {}))
msg = cast(str | None, event.get("text")) msg = cast(str | None, event.get("text"))
channel = cast(str | None, event.get("channel")) channel = cast(str | None, event.get("channel"))
channel_specific_logger = ChannelIdAdapter( channel_specific_logger = setup_logger(extra={SLACK_CHANNEL_ID: channel})
logger, extra={SLACK_CHANNEL_ID: channel}
)
# This should never happen, but we can't continue without a channel since # This should never happen, but we can't continue without a channel since
# we can't send a response without it # we can't send a response without it
if not channel: if not channel:
channel_specific_logger.error("Found message without channel - skipping") channel_specific_logger.warning("Found message without channel - skipping")
return False return False
if not msg: if not msg:
channel_specific_logger.error("Cannot respond to empty message - skipping") channel_specific_logger.warning(
"Cannot respond to empty message - skipping"
)
return False return False
if ( if (
@ -185,9 +184,8 @@ def prefilter_requests(req: SocketModeRequest, client: SocketModeClient) -> bool
if req.type == "slash_commands": if req.type == "slash_commands":
# Verify that there's an associated channel # Verify that there's an associated channel
channel = req.payload.get("channel_id") channel = req.payload.get("channel_id")
channel_specific_logger = ChannelIdAdapter( channel_specific_logger = setup_logger(extra={SLACK_CHANNEL_ID: channel})
logger, extra={SLACK_CHANNEL_ID: channel}
)
if not channel: if not channel:
channel_specific_logger.error( channel_specific_logger.error(
"Received DanswerBot command without channel - skipping" "Received DanswerBot command without channel - skipping"
@ -230,7 +228,7 @@ def process_feedback(req: SocketModeRequest, client: SocketModeClient) -> None:
) )
query_event_id, _, _ = decompose_action_id(feedback_id) query_event_id, _, _ = decompose_action_id(feedback_id)
logger.info(f"Successfully handled QA feedback for event: {query_event_id}") logger.notice(f"Successfully handled QA feedback for event: {query_event_id}")
def build_request_details( def build_request_details(
@ -247,15 +245,17 @@ def build_request_details(
msg = remove_danswer_bot_tag(msg, client=client.web_client) msg = remove_danswer_bot_tag(msg, client=client.web_client)
if DANSWER_BOT_REPHRASE_MESSAGE: if DANSWER_BOT_REPHRASE_MESSAGE:
logger.info(f"Rephrasing Slack message. Original message: {msg}") logger.notice(f"Rephrasing Slack message. Original message: {msg}")
try: try:
msg = rephrase_slack_message(msg) msg = rephrase_slack_message(msg)
logger.info(f"Rephrased message: {msg}") logger.notice(f"Rephrased message: {msg}")
except Exception as e: except Exception as e:
logger.error(f"Error while trying to rephrase the Slack message: {e}") logger.error(f"Error while trying to rephrase the Slack message: {e}")
else:
logger.notice(f"Received Slack message: {msg}")
if tagged: if tagged:
logger.info("User tagged DanswerBot") logger.debug("User tagged DanswerBot")
if thread_ts != message_ts and thread_ts is not None: if thread_ts != message_ts and thread_ts is not None:
thread_messages = read_slack_thread( thread_messages = read_slack_thread(
@ -437,7 +437,7 @@ def _initialize_socket_client(socket_client: SocketModeClient) -> None:
socket_client.socket_mode_request_listeners.append(process_slack_event) # type: ignore socket_client.socket_mode_request_listeners.append(process_slack_event) # type: ignore
# Establish a WebSocket connection to the Socket Mode servers # Establish a WebSocket connection to the Socket Mode servers
logger.info("Listening for messages from Slack...") logger.notice("Listening for messages from Slack...")
socket_client.connect() socket_client.connect()
@ -454,7 +454,7 @@ if __name__ == "__main__":
slack_bot_tokens: SlackBotTokens | None = None slack_bot_tokens: SlackBotTokens | None = None
socket_client: SocketModeClient | None = None socket_client: SocketModeClient | None = None
logger.info("Verifying query preprocessing (NLTK) data is downloaded") logger.notice("Verifying query preprocessing (NLTK) data is downloaded")
download_nltk_data() download_nltk_data()
while True: while True:
@ -463,7 +463,7 @@ if __name__ == "__main__":
if latest_slack_bot_tokens != slack_bot_tokens: if latest_slack_bot_tokens != slack_bot_tokens:
if slack_bot_tokens is not None: if slack_bot_tokens is not None:
logger.info("Slack Bot tokens have changed - reconnecting") logger.notice("Slack Bot tokens have changed - reconnecting")
else: else:
# This happens on the very first time the listener process comes up # This happens on the very first time the listener process comes up
# or the tokens have updated (set up for the first time) # or the tokens have updated (set up for the first time)

View File

@ -3,7 +3,6 @@ import random
import re import re
import string import string
import time import time
from collections.abc import MutableMapping
from typing import Any from typing import Any
from typing import cast from typing import cast
from typing import Optional from typing import Optional
@ -25,7 +24,6 @@ from danswer.configs.danswerbot_configs import DANSWER_BOT_NUM_RETRIES
from danswer.connectors.slack.utils import make_slack_api_rate_limited from danswer.connectors.slack.utils import make_slack_api_rate_limited
from danswer.connectors.slack.utils import SlackTextCleaner from danswer.connectors.slack.utils import SlackTextCleaner
from danswer.danswerbot.slack.constants import FeedbackVisibility from danswer.danswerbot.slack.constants import FeedbackVisibility
from danswer.danswerbot.slack.constants import SLACK_CHANNEL_ID
from danswer.danswerbot.slack.tokens import fetch_tokens from danswer.danswerbot.slack.tokens import fetch_tokens
from danswer.db.engine import get_sqlalchemy_engine from danswer.db.engine import get_sqlalchemy_engine
from danswer.db.users import get_user_by_email from danswer.db.users import get_user_by_email
@ -110,20 +108,6 @@ def remove_danswer_bot_tag(message_str: str, client: WebClient) -> str:
return re.sub(rf"<@{bot_tag_id}>\s", "", message_str) return re.sub(rf"<@{bot_tag_id}>\s", "", message_str)
class ChannelIdAdapter(logging.LoggerAdapter):
"""This is used to add the channel ID to all log messages
emitted in this file"""
def process(
self, msg: str, kwargs: MutableMapping[str, Any]
) -> tuple[str, MutableMapping[str, Any]]:
channel_id = self.extra.get(SLACK_CHANNEL_ID) if self.extra else None
if channel_id:
return f"[Channel ID: {channel_id}] {msg}", kwargs
else:
return msg, kwargs
def get_web_client() -> WebClient: def get_web_client() -> WebClient:
slack_tokens = fetch_tokens() slack_tokens = fetch_tokens()
return WebClient(token=slack_tokens.bot_token) return WebClient(token=slack_tokens.bot_token)

View File

@ -271,9 +271,9 @@ def delete_credential(
) )
if force: if force:
logger.info(f"Force deleting credential {credential_id}") logger.warning(f"Force deleting credential {credential_id}")
else: else:
logger.info(f"Deleting credential {credential_id}") logger.notice(f"Deleting credential {credential_id}")
db_session.delete(credential) db_session.delete(credential)
db_session.commit() db_session.commit()

View File

@ -335,7 +335,9 @@ def prepare_to_modify_documents(
yield transaction yield transaction
break break
except OperationalError as e: except OperationalError as e:
logger.info(f"Failed to acquire locks for documents, retrying. Error: {e}") logger.warning(
f"Failed to acquire locks for documents, retrying. Error: {e}"
)
time.sleep(retry_delay) time.sleep(retry_delay)

View File

@ -316,7 +316,7 @@ def query_vespa(
response_json: dict[str, Any] = response.json() response_json: dict[str, Any] = response.json()
if LOG_VESPA_TIMING_INFORMATION: if LOG_VESPA_TIMING_INFORMATION:
logger.info("Vespa timing info: %s", response_json.get("timing")) logger.debug("Vespa timing info: %s", response_json.get("timing"))
hits = response_json["root"].get("children", []) hits = response_json["root"].get("children", [])
for hit in hits: for hit in hits:

View File

@ -366,7 +366,7 @@ class VespaIndex(DocumentIndex):
) )
self._apply_updates_batched(processed_updates_requests) self._apply_updates_batched(processed_updates_requests)
logger.info( logger.debug(
"Finished updating Vespa documents in %.2f seconds", "Finished updating Vespa documents in %.2f seconds",
time.monotonic() - update_start, time.monotonic() - update_start,
) )

View File

@ -190,7 +190,7 @@ def pdf_to_text(file: IO[Any], pdf_pass: str | None = None) -> str:
except Exception: except Exception:
logger.error("Unable to decrypt pdf") logger.error("Unable to decrypt pdf")
else: else:
logger.info("No Password available to to decrypt pdf") logger.warning("No Password available to to decrypt pdf")
if not decrypt_success: if not decrypt_success:
# By user request, keep files that are unreadable just so they # By user request, keep files that are unreadable just so they

View File

@ -166,7 +166,7 @@ def index_doc_batch_with_handler(
index_attempt_metadata.num_exceptions += 1 index_attempt_metadata.num_exceptions += 1
if index_attempt_metadata.num_exceptions == INDEXING_EXCEPTION_LIMIT: if index_attempt_metadata.num_exceptions == INDEXING_EXCEPTION_LIMIT:
logger.info( logger.warning(
f"Maximum number of exceptions for this index attempt " f"Maximum number of exceptions for this index attempt "
f"({INDEXING_EXCEPTION_LIMIT}) has been reached. " f"({INDEXING_EXCEPTION_LIMIT}) has been reached. "
f"The next exception will abort the indexing attempt." f"The next exception will abort the indexing attempt."
@ -174,7 +174,7 @@ def index_doc_batch_with_handler(
elif index_attempt_metadata.num_exceptions > INDEXING_EXCEPTION_LIMIT: elif index_attempt_metadata.num_exceptions > INDEXING_EXCEPTION_LIMIT:
logger.warning( logger.warning(
f"Maximum number of exceptions for this index attempt " f"Maximum number of exceptions for this index attempt "
f"({INDEXING_EXCEPTION_LIMIT}) has been exceeded. Raising RuntimeError." f"({INDEXING_EXCEPTION_LIMIT}) has been exceeded."
) )
raise RuntimeError( raise RuntimeError(
f"Maximum exception limit of {INDEXING_EXCEPTION_LIMIT} exceeded." f"Maximum exception limit of {INDEXING_EXCEPTION_LIMIT} exceeded."

View File

@ -375,7 +375,7 @@ class Answer:
else None else None
) )
logger.info(f"Chosen tool: {chosen_tool_and_args}") logger.notice(f"Chosen tool: {chosen_tool_and_args}")
if not chosen_tool_and_args: if not chosen_tool_and_args:
prompt_builder.update_system_prompt( prompt_builder.update_system_prompt(

View File

@ -190,7 +190,7 @@ def _apply_pruning(
): ):
# If the section is just a little bit over, it is likely due to the additional tool message tokens # If the section is just a little bit over, it is likely due to the additional tool message tokens
# no need to record this, the content will be trimmed just in case # no need to record this, the content will be trimmed just in case
logger.info( logger.warning(
"Found more tokens in Section than expected, " "Found more tokens in Section than expected, "
"likely mismatch between embedding and LLM tokenizers. Trimming content..." "likely mismatch between embedding and LLM tokenizers. Trimming content..."
) )

View File

@ -170,11 +170,11 @@ def process_answer(
logger.debug("No answer extracted from raw output") logger.debug("No answer extracted from raw output")
return DanswerAnswer(answer=None), DanswerQuotes(quotes=[]) return DanswerAnswer(answer=None), DanswerQuotes(quotes=[])
logger.info(f"Answer: {answer}") logger.notice(f"Answer: {answer}")
if not quote_strings: if not quote_strings:
logger.debug("No quotes extracted from raw output") logger.debug("No quotes extracted from raw output")
return DanswerAnswer(answer=answer), DanswerQuotes(quotes=[]) return DanswerAnswer(answer=answer), DanswerQuotes(quotes=[])
logger.info(f"All quotes (including unmatched): {quote_strings}") logger.debug(f"All quotes (including unmatched): {quote_strings}")
quotes = match_quotes_to_docs(quote_strings, docs) quotes = match_quotes_to_docs(quote_strings, docs)
logger.debug(f"Final quotes: {quotes}") logger.debug(f"Final quotes: {quotes}")
@ -197,7 +197,7 @@ def _extract_quotes_from_completed_token_stream(
) -> DanswerQuotes: ) -> DanswerQuotes:
answer, quotes = process_answer(model_output, context_docs, is_json_prompt) answer, quotes = process_answer(model_output, context_docs, is_json_prompt)
if answer: if answer:
logger.info(answer) logger.notice(answer)
elif model_output: elif model_output:
logger.warning("Answer extraction from model output failed.") logger.warning("Answer extraction from model output failed.")

View File

@ -226,7 +226,7 @@ class DefaultMultiLLM(LLM):
self._model_kwargs = model_kwargs self._model_kwargs = model_kwargs
def log_model_configs(self) -> None: def log_model_configs(self) -> None:
logger.info(f"Config: {self.config}") logger.debug(f"Config: {self.config}")
def _completion( def _completion(
self, self,

View File

@ -75,6 +75,6 @@ def load_llm_providers(db_session: Session) -> None:
) )
llm_provider = upsert_llm_provider(db_session, llm_provider_request) llm_provider = upsert_llm_provider(db_session, llm_provider_request)
update_default_provider(db_session, llm_provider.id) update_default_provider(db_session, llm_provider.id)
logger.info( logger.notice(
f"Migrated LLM provider from env variables for provider '{GEN_AI_MODEL_PROVIDER}'" f"Migrated LLM provider from env variables for provider '{GEN_AI_MODEL_PROVIDER}'"
) )

View File

@ -111,7 +111,6 @@ from danswer.tools.built_in_tools import auto_add_search_tool_to_personas
from danswer.tools.built_in_tools import load_builtin_tools from danswer.tools.built_in_tools import load_builtin_tools
from danswer.tools.built_in_tools import refresh_built_in_tools_cache from danswer.tools.built_in_tools import refresh_built_in_tools_cache
from danswer.utils.logger import setup_logger from danswer.utils.logger import setup_logger
from danswer.utils.logger import setup_uvicorn_logger
from danswer.utils.telemetry import optional_telemetry from danswer.utils.telemetry import optional_telemetry
from danswer.utils.telemetry import RecordType from danswer.utils.telemetry import RecordType
from danswer.utils.variable_functionality import fetch_versioned_implementation from danswer.utils.variable_functionality import fetch_versioned_implementation
@ -127,7 +126,6 @@ from shared_configs.enums import RerankerProvider
logger = setup_logger() logger = setup_logger()
setup_uvicorn_logger()
def validation_exception_handler(request: Request, exc: Exception) -> JSONResponse: def validation_exception_handler(request: Request, exc: Exception) -> JSONResponse:
@ -179,22 +177,22 @@ def include_router_with_global_prefix_prepended(
def setup_postgres(db_session: Session) -> None: def setup_postgres(db_session: Session) -> None:
logger.info("Verifying default connector/credential exist.") logger.notice("Verifying default connector/credential exist.")
create_initial_public_credential(db_session) create_initial_public_credential(db_session)
create_initial_default_connector(db_session) create_initial_default_connector(db_session)
associate_default_cc_pair(db_session) associate_default_cc_pair(db_session)
logger.info("Verifying default standard answer category exists.") logger.notice("Verifying default standard answer category exists.")
create_initial_default_standard_answer_category(db_session) create_initial_default_standard_answer_category(db_session)
logger.info("Loading LLM providers from env variables") logger.notice("Loading LLM providers from env variables")
load_llm_providers(db_session) load_llm_providers(db_session)
logger.info("Loading default Prompts and Personas") logger.notice("Loading default Prompts and Personas")
delete_old_default_personas(db_session) delete_old_default_personas(db_session)
load_chat_yamls() load_chat_yamls()
logger.info("Loading built-in tools") logger.notice("Loading built-in tools")
load_builtin_tools(db_session) load_builtin_tools(db_session)
refresh_built_in_tools_cache(db_session) refresh_built_in_tools_cache(db_session)
auto_add_search_tool_to_personas(db_session) auto_add_search_tool_to_personas(db_session)
@ -238,7 +236,7 @@ def setup_vespa(
) )
break break
except Exception: except Exception:
logger.info(f"Waiting on Vespa, retrying in {wait_time} seconds...") logger.notice(f"Waiting on Vespa, retrying in {wait_time} seconds...")
time.sleep(wait_time) time.sleep(wait_time)
@ -279,25 +277,27 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
# Expire all old embedding models indexing attempts, technically redundant # Expire all old embedding models indexing attempts, technically redundant
cancel_indexing_attempts_past_model(db_session) cancel_indexing_attempts_past_model(db_session)
logger.info(f'Using Embedding model: "{db_embedding_model.model_name}"') logger.notice(f'Using Embedding model: "{db_embedding_model.model_name}"')
if db_embedding_model.query_prefix or db_embedding_model.passage_prefix: if db_embedding_model.query_prefix or db_embedding_model.passage_prefix:
logger.info(f'Query embedding prefix: "{db_embedding_model.query_prefix}"') logger.notice(
logger.info( f'Query embedding prefix: "{db_embedding_model.query_prefix}"'
)
logger.notice(
f'Passage embedding prefix: "{db_embedding_model.passage_prefix}"' f'Passage embedding prefix: "{db_embedding_model.passage_prefix}"'
) )
search_settings = get_search_settings() search_settings = get_search_settings()
if search_settings: if search_settings:
if not search_settings.disable_rerank_for_streaming: if not search_settings.disable_rerank_for_streaming:
logger.info("Reranking is enabled.") logger.notice("Reranking is enabled.")
if search_settings.multilingual_expansion: if search_settings.multilingual_expansion:
logger.info( logger.notice(
f"Multilingual query expansion is enabled with {search_settings.multilingual_expansion}." f"Multilingual query expansion is enabled with {search_settings.multilingual_expansion}."
) )
else: else:
if DEFAULT_CROSS_ENCODER_MODEL_NAME: if DEFAULT_CROSS_ENCODER_MODEL_NAME:
logger.info("Reranking is enabled.") logger.notice("Reranking is enabled.")
if not DEFAULT_CROSS_ENCODER_MODEL_NAME: if not DEFAULT_CROSS_ENCODER_MODEL_NAME:
raise ValueError("No reranking model specified.") raise ValueError("No reranking model specified.")
search_settings = SavedSearchSettings( search_settings = SavedSearchSettings(
@ -322,7 +322,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
if search_settings.rerank_model_name and not search_settings.provider_type: if search_settings.rerank_model_name and not search_settings.provider_type:
warm_up_cross_encoder(search_settings.rerank_model_name) warm_up_cross_encoder(search_settings.rerank_model_name)
logger.info("Verifying query preprocessing (NLTK) data is downloaded") logger.notice("Verifying query preprocessing (NLTK) data is downloaded")
download_nltk_data() download_nltk_data()
# setup Postgres with default credential, llm providers, etc. # setup Postgres with default credential, llm providers, etc.
@ -333,7 +333,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
mark_reindex_flag(db_session) mark_reindex_flag(db_session)
# ensure Vespa is setup correctly # ensure Vespa is setup correctly
logger.info("Verifying Document Index(s) is/are available.") logger.notice("Verifying Document Index(s) is/are available.")
document_index = get_default_document_index( document_index = get_default_document_index(
primary_index_name=db_embedding_model.index_name, primary_index_name=db_embedding_model.index_name,
secondary_index_name=secondary_db_embedding_model.index_name secondary_index_name=secondary_db_embedding_model.index_name
@ -342,7 +342,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
) )
setup_vespa(document_index, db_embedding_model, secondary_db_embedding_model) setup_vespa(document_index, db_embedding_model, secondary_db_embedding_model)
logger.info(f"Model Server: http://{MODEL_SERVER_HOST}:{MODEL_SERVER_PORT}") logger.notice(f"Model Server: http://{MODEL_SERVER_HOST}:{MODEL_SERVER_PORT}")
if db_embedding_model.cloud_provider_id is None: if db_embedding_model.cloud_provider_id is None:
warm_up_bi_encoder( warm_up_bi_encoder(
embedding_model=db_embedding_model, embedding_model=db_embedding_model,
@ -485,11 +485,11 @@ app = fetch_versioned_implementation(module="danswer.main", attribute="get_appli
if __name__ == "__main__": if __name__ == "__main__":
logger.info( logger.notice(
f"Starting Danswer Backend version {__version__} on http://{APP_HOST}:{str(APP_PORT)}/" f"Starting Danswer Backend version {__version__} on http://{APP_HOST}:{str(APP_PORT)}/"
) )
if global_version.get_is_ee_version(): if global_version.get_is_ee_version():
logger.info("Running Enterprise Edition") logger.notice("Running Enterprise Edition")
uvicorn.run(app, host=APP_HOST, port=APP_PORT) uvicorn.run(app, host=APP_HOST, port=APP_PORT)

View File

@ -39,7 +39,7 @@ def _log_top_section_links(search_flow: str, sections: list[InferenceSection]) -
else "No Link" else "No Link"
for section in sections for section in sections
] ]
logger.info(f"Top links from {search_flow} search: {', '.join(top_links)}") logger.debug(f"Top links from {search_flow} search: {', '.join(top_links)}")
def cleanup_chunks(chunks: list[InferenceChunkUncleaned]) -> list[InferenceChunk]: def cleanup_chunks(chunks: list[InferenceChunkUncleaned]) -> list[InferenceChunk]:

View File

@ -265,7 +265,7 @@ def retrieve_chunks(
top_chunks = combine_retrieval_results(parallel_search_results) top_chunks = combine_retrieval_results(parallel_search_results)
if not top_chunks: if not top_chunks:
logger.info( logger.warning(
f"{query.search_type.value.capitalize()} search returned no results " f"{query.search_type.value.capitalize()} search returned no results "
f"with filters: {query.filters}" f"with filters: {query.filters}"
) )

View File

@ -16,7 +16,7 @@ def add_latency_logging_middleware(app: FastAPI, logger: logging.LoggerAdapter)
start_time = time.monotonic() start_time = time.monotonic()
response = await call_next(request) response = await call_next(request)
process_time = time.monotonic() - start_time process_time = time.monotonic() - start_time
logger.info( logger.debug(
f"Path: {request.url.path} - Method: {request.method} - " f"Path: {request.url.path} - Method: {request.method} - "
f"Status Code: {response.status_code} - Time: {process_time:.4f} secs" f"Status Code: {response.status_code} - Time: {process_time:.4f} secs"
) )

View File

@ -54,7 +54,7 @@ def admin_search(
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> AdminSearchResponse: ) -> AdminSearchResponse:
query = question.query query = question.query
logger.info(f"Received admin search query: {query}") logger.notice(f"Received admin search query: {query}")
user_acl_filters = build_access_filters_for_user(user, db_session) user_acl_filters = build_access_filters_for_user(user, db_session)
final_filters = IndexFilters( final_filters = IndexFilters(
source_type=question.filters.source_type, source_type=question.filters.source_type,
@ -122,7 +122,7 @@ def query_validation(
# Note if weak model prompt is chosen, this check does not occur and will simply return that # Note if weak model prompt is chosen, this check does not occur and will simply return that
# the query is valid, this is because weaker models cannot really handle this task well. # the query is valid, this is because weaker models cannot really handle this task well.
# Additionally, some weak model servers cannot handle concurrent inferences. # Additionally, some weak model servers cannot handle concurrent inferences.
logger.info(f"Validating query: {simple_query.query}") logger.notice(f"Validating query: {simple_query.query}")
reasoning, answerable = get_query_answerability(simple_query.query) reasoning, answerable = get_query_answerability(simple_query.query)
return QueryValidationResponse(reasoning=reasoning, answerable=answerable) return QueryValidationResponse(reasoning=reasoning, answerable=answerable)
@ -231,7 +231,7 @@ def stream_query_validation(
# Note if weak model prompt is chosen, this check does not occur and will simply return that # Note if weak model prompt is chosen, this check does not occur and will simply return that
# the query is valid, this is because weaker models cannot really handle this task well. # the query is valid, this is because weaker models cannot really handle this task well.
# Additionally, some weak model servers cannot handle concurrent inferences. # Additionally, some weak model servers cannot handle concurrent inferences.
logger.info(f"Validating query: {simple_query.query}") logger.notice(f"Validating query: {simple_query.query}")
return StreamingResponse( return StreamingResponse(
stream_query_answerability(simple_query.query), media_type="application/json" stream_query_answerability(simple_query.query), media_type="application/json"
) )
@ -245,7 +245,7 @@ def get_answer_with_quote(
) -> StreamingResponse: ) -> StreamingResponse:
query = query_request.messages[0].message query = query_request.messages[0].message
logger.info(f"Received query for one shot answer with quotes: {query}") logger.notice(f"Received query for one shot answer with quotes: {query}")
packets = stream_search_answer( packets = stream_search_answer(
query_req=query_request, query_req=query_request,

View File

@ -123,7 +123,7 @@ def _is_rate_limited(
def any_rate_limit_exists() -> bool: def any_rate_limit_exists() -> bool:
"""Checks if any rate limit exists in the database. Is cached, so that if no rate limits """Checks if any rate limit exists in the database. Is cached, so that if no rate limits
are setup, we don't have any effect on average query latency.""" are setup, we don't have any effect on average query latency."""
logger.info("Checking for any rate limits...") logger.debug("Checking for any rate limits...")
with get_session_context_manager() as db_session: with get_session_context_manager() as db_session:
return ( return (
db_session.scalar( db_session.scalar(

View File

@ -77,7 +77,7 @@ def load_builtin_tools(db_session: Session) -> None:
tool.name = tool_name tool.name = tool_name
tool.description = tool_info["description"] tool.description = tool_info["description"]
tool.display_name = tool_info["display_name"] tool.display_name = tool_info["display_name"]
logger.info(f"Updated tool: {tool_name}") logger.notice(f"Updated tool: {tool_name}")
else: else:
# Add new tool # Add new tool
new_tool = ToolDBModel( new_tool = ToolDBModel(
@ -87,17 +87,17 @@ def load_builtin_tools(db_session: Session) -> None:
in_code_tool_id=tool_info["in_code_tool_id"], in_code_tool_id=tool_info["in_code_tool_id"],
) )
db_session.add(new_tool) db_session.add(new_tool)
logger.info(f"Added new tool: {tool_name}") logger.notice(f"Added new tool: {tool_name}")
# Remove tools that are no longer in BUILT_IN_TOOLS # Remove tools that are no longer in BUILT_IN_TOOLS
built_in_ids = {tool_info["in_code_tool_id"] for tool_info in BUILT_IN_TOOLS} built_in_ids = {tool_info["in_code_tool_id"] for tool_info in BUILT_IN_TOOLS}
for tool_id, tool in list(in_code_tool_id_to_tool.items()): for tool_id, tool in list(in_code_tool_id_to_tool.items()):
if tool_id not in built_in_ids: if tool_id not in built_in_ids:
db_session.delete(tool) db_session.delete(tool)
logger.info(f"Removed tool no longer in built-in list: {tool.name}") logger.notice(f"Removed tool no longer in built-in list: {tool.name}")
db_session.commit() db_session.commit()
logger.info("All built-in tools are loaded/verified.") logger.notice("All built-in tools are loaded/verified.")
def auto_add_search_tool_to_personas(db_session: Session) -> None: def auto_add_search_tool_to_personas(db_session: Session) -> None:
@ -140,11 +140,11 @@ def auto_add_search_tool_to_personas(db_session: Session) -> None:
for persona in personas_to_update: for persona in personas_to_update:
if search_tool not in persona.tools: if search_tool not in persona.tools:
persona.tools.append(search_tool) persona.tools.append(search_tool)
logger.info(f"Added SearchTool to Persona ID: {persona.id}") logger.notice(f"Added SearchTool to Persona ID: {persona.id}")
# Commit changes to the database # Commit changes to the database
db_session.commit() db_session.commit()
logger.info("Completed adding SearchTool to relevant Personas.") logger.notice("Completed adding SearchTool to relevant Personas.")
_built_in_tools_cache: dict[int, Type[Tool]] | None = None _built_in_tools_cache: dict[int, Type[Tool]] | None = None

View File

@ -1,8 +1,10 @@
import logging import logging
import os import os
from collections.abc import MutableMapping from collections.abc import MutableMapping
from logging.handlers import RotatingFileHandler
from typing import Any from typing import Any
from danswer.configs.constants import SLACK_CHANNEL_ID
from shared_configs.configs import DEV_LOGGING_ENABLED from shared_configs.configs import DEV_LOGGING_ENABLED
from shared_configs.configs import LOG_FILE_NAME from shared_configs.configs import LOG_FILE_NAME
from shared_configs.configs import LOG_LEVEL from shared_configs.configs import LOG_LEVEL
@ -48,14 +50,21 @@ class DanswerLoggingAdapter(logging.LoggerAdapter):
# If this is an indexing job, add the attempt ID to the log message # If this is an indexing job, add the attempt ID to the log message
# This helps filter the logs for this specific indexing # This helps filter the logs for this specific indexing
attempt_id = IndexAttemptSingleton.get_index_attempt_id() attempt_id = IndexAttemptSingleton.get_index_attempt_id()
if attempt_id is None: if attempt_id is not None:
return msg, kwargs msg = f"[Attempt ID: {attempt_id}] {msg}"
return f"[Attempt ID: {attempt_id}] {msg}", kwargs # For Slack Bot, logs the channel relevant to the request
channel_id = self.extra.get(SLACK_CHANNEL_ID) if self.extra else None
if channel_id:
msg = f"[Channel ID: {channel_id}] {msg}"
def notice(self, msg: str, *args: Any, **kwargs: Any) -> None: return msg, kwargs
def notice(self, msg: Any, *args: Any, **kwargs: Any) -> None:
# Stacklevel is set to 2 to point to the actual caller of notice instead of here # Stacklevel is set to 2 to point to the actual caller of notice instead of here
self.log(logging.getLevelName("NOTICE"), msg, *args, **kwargs, stacklevel=2) self.log(
logging.getLevelName("NOTICE"), str(msg), *args, **kwargs, stacklevel=2
)
class ColoredFormatter(logging.Formatter): class ColoredFormatter(logging.Formatter):
@ -95,12 +104,13 @@ def get_standard_formatter() -> ColoredFormatter:
def setup_logger( def setup_logger(
name: str = __name__, name: str = __name__,
log_level: int = get_log_level_from_str(), log_level: int = get_log_level_from_str(),
extra: MutableMapping[str, Any] | None = None,
) -> DanswerLoggingAdapter: ) -> DanswerLoggingAdapter:
logger = logging.getLogger(name) logger = logging.getLogger(name)
# If the logger already has handlers, assume it was already configured and return it. # If the logger already has handlers, assume it was already configured and return it.
if logger.handlers: if logger.handlers:
return DanswerLoggingAdapter(logger) return DanswerLoggingAdapter(logger, extra=extra)
logger.setLevel(log_level) logger.setLevel(log_level)
@ -112,6 +122,12 @@ def setup_logger(
logger.addHandler(handler) logger.addHandler(handler)
uvicorn_logger = logging.getLogger("uvicorn.access")
if uvicorn_logger:
uvicorn_logger.handlers = []
uvicorn_logger.addHandler(handler)
uvicorn_logger.setLevel(log_level)
is_containerized = os.path.exists("/.dockerenv") is_containerized = os.path.exists("/.dockerenv")
if LOG_FILE_NAME and (is_containerized or DEV_LOGGING_ENABLED): if LOG_FILE_NAME and (is_containerized or DEV_LOGGING_ENABLED):
log_levels = ["debug", "info", "notice"] log_levels = ["debug", "info", "notice"]
@ -121,7 +137,7 @@ def setup_logger(
if is_containerized if is_containerized
else f"./log/{LOG_FILE_NAME}_{level}.log" else f"./log/{LOG_FILE_NAME}_{level}.log"
) )
file_handler = logging.handlers.RotatingFileHandler( file_handler = RotatingFileHandler(
file_name, file_name,
maxBytes=25 * 1024 * 1024, # 25 MB maxBytes=25 * 1024 * 1024, # 25 MB
backupCount=5, # Keep 5 backup files backupCount=5, # Keep 5 backup files
@ -130,17 +146,9 @@ def setup_logger(
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
logger.addHandler(file_handler) logger.addHandler(file_handler)
if uvicorn_logger:
uvicorn_logger.addHandler(file_handler)
logger.notice = lambda msg, *args, **kwargs: logger.log(logging.getLevelName("NOTICE"), msg, *args, **kwargs) # type: ignore logger.notice = lambda msg, *args, **kwargs: logger.log(logging.getLevelName("NOTICE"), msg, *args, **kwargs) # type: ignore
return DanswerLoggingAdapter(logger) return DanswerLoggingAdapter(logger, extra=extra)
def setup_uvicorn_logger() -> None:
logger = logging.getLogger("uvicorn.access")
handler = logging.StreamHandler()
handler.setLevel(get_log_level_from_str(LOG_LEVEL))
formatter = get_standard_formatter()
handler.setFormatter(formatter)
logger.handlers = [handler]

View File

@ -37,7 +37,8 @@ def log_function_time(
if debug_only: if debug_only:
logger.debug(final_log) logger.debug(final_log)
else: else:
logger.info(final_log) # These are generally more important logs so the level is a bit higher
logger.notice(final_log)
if not print_only: if not print_only:
optional_telemetry( optional_telemetry(

View File

@ -25,7 +25,7 @@ global_version = DanswerVersion()
def set_is_ee_based_on_env_variable() -> None: def set_is_ee_based_on_env_variable() -> None:
if ENTERPRISE_EDITION_ENABLED and not global_version.get_is_ee_version(): if ENTERPRISE_EDITION_ENABLED and not global_version.get_is_ee_version():
logger.info("Enterprise Edition enabled") logger.notice("Enterprise Edition enabled")
global_version.set_ee() global_version.set_ee()

View File

@ -19,7 +19,7 @@ logger = setup_logger()
def verify_auth_setting() -> None: def verify_auth_setting() -> None:
# All the Auth flows are valid for EE version # All the Auth flows are valid for EE version
logger.info(f"Using Auth Type: {AUTH_TYPE.value}") logger.notice(f"Using Auth Type: {AUTH_TYPE.value}")
async def optional_user_( async def optional_user_(

View File

@ -215,7 +215,7 @@ def permission_loop(delay: int = 60, num_workers: int = NUM_PERMISSION_WORKERS)
def update__main() -> None: def update__main() -> None:
logger.info("Starting Permission Syncing Loop") logger.notice("Starting Permission Syncing Loop")
init_sqlalchemy_engine(POSTGRES_PERMISSIONS_APP_NAME) init_sqlalchemy_engine(POSTGRES_PERMISSIONS_APP_NAME)
permission_loop() permission_loop()

View File

@ -85,7 +85,7 @@ def upload_logo(
content: IO[Any] content: IO[Any]
if isinstance(file, str): if isinstance(file, str):
logger.info(f"Uploading logo from local path {file}") logger.notice(f"Uploading logo from local path {file}")
if not os.path.isfile(file) or not is_valid_file_type(file): if not os.path.isfile(file) or not is_valid_file_type(file):
logger.error( logger.error(
"Invalid file type- only .png, .jpg, and .jpeg files are allowed" "Invalid file type- only .png, .jpg, and .jpeg files are allowed"
@ -99,7 +99,7 @@ def upload_logo(
file_type = guess_file_type(file) file_type = guess_file_type(file)
else: else:
logger.info("Uploading logo from uploaded file") logger.notice("Uploading logo from uploaded file")
if not file.filename or not is_valid_file_type(file.filename): if not file.filename or not is_valid_file_type(file.filename):
raise HTTPException( raise HTTPException(
status_code=400, status_code=400,

View File

@ -72,7 +72,7 @@ def handle_simplified_chat_message(
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> ChatBasicResponse: ) -> ChatBasicResponse:
"""This is a Non-Streaming version that only gives back a minimal set of information""" """This is a Non-Streaming version that only gives back a minimal set of information"""
logger.info(f"Received new simple api chat message: {chat_message_req.message}") logger.notice(f"Received new simple api chat message: {chat_message_req.message}")
if not chat_message_req.message: if not chat_message_req.message:
raise HTTPException(status_code=400, detail="Empty chat message is invalid") raise HTTPException(status_code=400, detail="Empty chat message is invalid")
@ -170,7 +170,7 @@ def handle_send_message_simple_with_history(
query = req.messages[-1].message query = req.messages[-1].message
msg_history = req.messages[:-1] msg_history = req.messages[:-1]
logger.info(f"Received new simple with history chat message: {query}") logger.notice(f"Received new simple with history chat message: {query}")
user_id = user.id if user is not None else None user_id = user.id if user is not None else None
chat_session = create_chat_session( chat_session = create_chat_session(

View File

@ -51,7 +51,7 @@ def handle_search_request(
) -> DocumentSearchResponse: ) -> DocumentSearchResponse:
"""Simple search endpoint, does not create a new message or records in the DB""" """Simple search endpoint, does not create a new message or records in the DB"""
query = search_request.message query = search_request.message
logger.info(f"Received document search query: {query}") logger.notice(f"Received document search query: {query}")
llm, fast_llm = get_default_llms() llm, fast_llm = get_default_llms()
search_pipeline = SearchPipeline( search_pipeline = SearchPipeline(
@ -130,7 +130,7 @@ def get_answer_with_quote(
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> OneShotQAResponse: ) -> OneShotQAResponse:
query = query_request.messages[0].message query = query_request.messages[0].message
logger.info(f"Received query for one shot answer API with quotes: {query}") logger.notice(f"Received query for one shot answer API with quotes: {query}")
persona = get_persona_by_id( persona = get_persona_by_id(
persona_id=query_request.persona_id, persona_id=query_request.persona_id,

View File

@ -50,7 +50,7 @@ async def upsert_saml_user(email: str) -> User:
try: try:
return await user_manager.get_by_email(email) return await user_manager.get_by_email(email)
except exceptions.UserNotExists: except exceptions.UserNotExists:
logger.info("Creating user from SAML login") logger.notice("Creating user from SAML login")
user_count = await get_user_count() user_count = await get_user_count()
role = UserRole.ADMIN if user_count == 0 else UserRole.BASIC role = UserRole.ADMIN if user_count == 0 else UserRole.BASIC

View File

@ -49,7 +49,7 @@ def _seed_llms(
db_session: Session, llm_upsert_requests: list[LLMProviderUpsertRequest] db_session: Session, llm_upsert_requests: list[LLMProviderUpsertRequest]
) -> None: ) -> None:
if llm_upsert_requests: if llm_upsert_requests:
logger.info("Seeding LLMs") logger.notice("Seeding LLMs")
seeded_providers = [ seeded_providers = [
upsert_llm_provider(db_session, llm_upsert_request) upsert_llm_provider(db_session, llm_upsert_request)
for llm_upsert_request in llm_upsert_requests for llm_upsert_request in llm_upsert_requests
@ -59,7 +59,7 @@ def _seed_llms(
def _seed_personas(db_session: Session, personas: list[CreatePersonaRequest]) -> None: def _seed_personas(db_session: Session, personas: list[CreatePersonaRequest]) -> None:
if personas: if personas:
logger.info("Seeding Personas") logger.notice("Seeding Personas")
for persona in personas: for persona in personas:
upsert_persona( upsert_persona(
user=None, # Seeding is done as admin user=None, # Seeding is done as admin
@ -83,31 +83,31 @@ def _seed_personas(db_session: Session, personas: list[CreatePersonaRequest]) ->
def _seed_settings(settings: Settings) -> None: def _seed_settings(settings: Settings) -> None:
logger.info("Seeding Settings") logger.notice("Seeding Settings")
try: try:
settings.check_validity() settings.check_validity()
store_base_settings(settings) store_base_settings(settings)
logger.info("Successfully seeded Settings") logger.notice("Successfully seeded Settings")
except ValueError as e: except ValueError as e:
logger.error(f"Failed to seed Settings: {str(e)}") logger.error(f"Failed to seed Settings: {str(e)}")
def _seed_enterprise_settings(seed_config: SeedConfiguration) -> None: def _seed_enterprise_settings(seed_config: SeedConfiguration) -> None:
if seed_config.enterprise_settings is not None: if seed_config.enterprise_settings is not None:
logger.info("Seeding enterprise settings") logger.notice("Seeding enterprise settings")
store_ee_settings(seed_config.enterprise_settings) store_ee_settings(seed_config.enterprise_settings)
def _seed_logo(db_session: Session, logo_path: str | None) -> None: def _seed_logo(db_session: Session, logo_path: str | None) -> None:
if logo_path: if logo_path:
logger.info("Uploading logo") logger.notice("Uploading logo")
upload_logo(db_session=db_session, file=logo_path) upload_logo(db_session=db_session, file=logo_path)
def _seed_analytics_script(seed_config: SeedConfiguration) -> None: def _seed_analytics_script(seed_config: SeedConfiguration) -> None:
custom_analytics_secret_key = os.environ.get("CUSTOM_ANALYTICS_SECRET_KEY") custom_analytics_secret_key = os.environ.get("CUSTOM_ANALYTICS_SECRET_KEY")
if seed_config.analytics_script_path and custom_analytics_secret_key: if seed_config.analytics_script_path and custom_analytics_secret_key:
logger.info("Seeding analytics script") logger.notice("Seeding analytics script")
try: try:
with open(seed_config.analytics_script_path, "r") as file: with open(seed_config.analytics_script_path, "r") as file:
script_content = file.read() script_content = file.read()
@ -130,7 +130,7 @@ def get_seed_config() -> SeedConfiguration | None:
def seed_db() -> None: def seed_db() -> None:
seed_config = _parse_env() seed_config = _parse_env()
if seed_config is None: if seed_config is None:
logger.info("No seeding configuration file passed") logger.debug("No seeding configuration file passed")
return return
with get_session_context_manager() as db_session: with get_session_context_manager() as db_session:

View File

@ -48,7 +48,7 @@ def get_local_intent_model(
logger.warning(f"Failed to load model directly: {e}") logger.warning(f"Failed to load model directly: {e}")
try: try:
# Attempt to download the model snapshot # Attempt to download the model snapshot
logger.info(f"Downloading model snapshot for {model_name_or_path}") logger.notice(f"Downloading model snapshot for {model_name_or_path}")
local_path = snapshot_download(repo_id=model_name_or_path, revision=tag) local_path = snapshot_download(repo_id=model_name_or_path, revision=tag)
_INTENT_MODEL = HybridClassifier.from_pretrained(local_path) _INTENT_MODEL = HybridClassifier.from_pretrained(local_path)
except Exception as e: except Exception as e:
@ -60,7 +60,7 @@ def get_local_intent_model(
def warm_up_intent_model() -> None: def warm_up_intent_model() -> None:
logger.info(f"Warming up Intent Model: {INTENT_MODEL_VERSION}") logger.notice(f"Warming up Intent Model: {INTENT_MODEL_VERSION}")
intent_tokenizer = get_intent_model_tokenizer() intent_tokenizer = get_intent_model_tokenizer()
tokens = intent_tokenizer( tokens = intent_tokenizer(
MODEL_WARM_UP_STRING, return_tensors="pt", truncation=True, padding=True MODEL_WARM_UP_STRING, return_tensors="pt", truncation=True, padding=True

View File

@ -208,7 +208,7 @@ def get_embedding_model(
_GLOBAL_MODELS_DICT = {} _GLOBAL_MODELS_DICT = {}
if model_name not in _GLOBAL_MODELS_DICT: if model_name not in _GLOBAL_MODELS_DICT:
logger.info(f"Loading {model_name}") logger.notice(f"Loading {model_name}")
# Some model architectures that aren't built into the Transformers or Sentence # Some model architectures that aren't built into the Transformers or Sentence
# Transformer need to be downloaded to be loaded locally. This does not mean # Transformer need to be downloaded to be loaded locally. This does not mean
# data is sent to remote servers for inference, however the remote code can # data is sent to remote servers for inference, however the remote code can
@ -230,7 +230,7 @@ def get_local_reranking_model(
) -> CrossEncoder: ) -> CrossEncoder:
global _RERANK_MODEL global _RERANK_MODEL
if _RERANK_MODEL is None: if _RERANK_MODEL is None:
logger.info(f"Loading {model_name}") logger.notice(f"Loading {model_name}")
model = CrossEncoder(model_name) model = CrossEncoder(model_name)
_RERANK_MODEL = model _RERANK_MODEL = model
return _RERANK_MODEL return _RERANK_MODEL

View File

@ -54,23 +54,23 @@ def _move_files_recursively(source: Path, dest: Path, overwrite: bool = False) -
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator: async def lifespan(app: FastAPI) -> AsyncGenerator:
if torch.cuda.is_available(): if torch.cuda.is_available():
logger.info("GPU is available") logger.notice("GPU is available")
else: else:
logger.info("GPU is not available") logger.notice("GPU is not available")
if TEMP_HF_CACHE_PATH.is_dir(): if TEMP_HF_CACHE_PATH.is_dir():
logger.info("Moving contents of temp_huggingface to huggingface cache.") logger.notice("Moving contents of temp_huggingface to huggingface cache.")
_move_files_recursively(TEMP_HF_CACHE_PATH, HF_CACHE_PATH) _move_files_recursively(TEMP_HF_CACHE_PATH, HF_CACHE_PATH)
shutil.rmtree(TEMP_HF_CACHE_PATH, ignore_errors=True) shutil.rmtree(TEMP_HF_CACHE_PATH, ignore_errors=True)
logger.info("Moved contents of temp_huggingface to huggingface cache.") logger.notice("Moved contents of temp_huggingface to huggingface cache.")
torch.set_num_threads(max(MIN_THREADS_ML_MODELS, torch.get_num_threads())) torch.set_num_threads(max(MIN_THREADS_ML_MODELS, torch.get_num_threads()))
logger.info(f"Torch Threads: {torch.get_num_threads()}") logger.notice(f"Torch Threads: {torch.get_num_threads()}")
if not INDEXING_ONLY: if not INDEXING_ONLY:
warm_up_intent_model() warm_up_intent_model()
else: else:
logger.info("This model server should only run document indexing.") logger.notice("This model server should only run document indexing.")
yield yield
@ -91,8 +91,8 @@ app = get_model_app()
if __name__ == "__main__": if __name__ == "__main__":
logger.info( logger.notice(
f"Starting Danswer Model Server on http://{MODEL_SERVER_ALLOWED_HOST}:{str(MODEL_SERVER_PORT)}/" f"Starting Danswer Model Server on http://{MODEL_SERVER_ALLOWED_HOST}:{str(MODEL_SERVER_PORT)}/"
) )
logger.info(f"Model Server Version: {__version__}") logger.notice(f"Model Server Version: {__version__}")
uvicorn.run(app, host=MODEL_SERVER_ALLOWED_HOST, port=MODEL_SERVER_PORT) uvicorn.run(app, host=MODEL_SERVER_ALLOWED_HOST, port=MODEL_SERVER_PORT)

View File

@ -32,7 +32,7 @@ def simple_log_function_time(
if debug_only: if debug_only:
logger.debug(final_log) logger.debug(final_log)
else: else:
logger.info(final_log) logger.notice(final_log)
return result return result

View File

@ -119,7 +119,7 @@ def _unsafe_deletion(
db_session.delete(connector) db_session.delete(connector)
db_session.commit() db_session.commit()
logger.info( logger.notice(
"Successfully deleted connector_credential_pair with connector_id:" "Successfully deleted connector_credential_pair with connector_id:"
f" '{connector_id}' and credential_id: '{credential_id}'. Deleted {num_docs_deleted} docs." f" '{connector_id}' and credential_id: '{credential_id}'. Deleted {num_docs_deleted} docs."
) )
@ -133,10 +133,10 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None:
Are you SURE you want to continue? (enter 'Y' to continue): " Are you SURE you want to continue? (enter 'Y' to continue): "
) )
if user_input != "Y": if user_input != "Y":
logger.info(f"You entered {user_input}. Exiting!") logger.notice(f"You entered {user_input}. Exiting!")
return return
logger.info("Getting connector credential pair") logger.notice("Getting connector credential pair")
cc_pair = get_connector_credential_pair_from_id(cc_pair_id, db_session) cc_pair = get_connector_credential_pair_from_id(cc_pair_id, db_session)
if not cc_pair: if not cc_pair:
@ -160,7 +160,7 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None:
) )
return return
logger.info("Cancelling indexing attempt for the connector") logger.notice("Cancelling indexing attempt for the connector")
cancel_indexing_attempts_for_ccpair( cancel_indexing_attempts_for_ccpair(
cc_pair_id=cc_pair_id, db_session=db_session, include_secondary_index=True cc_pair_id=cc_pair_id, db_session=db_session, include_secondary_index=True
) )
@ -183,7 +183,7 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None:
else [] else []
) )
try: try:
logger.info("Deleting information from Vespa and Postgres") logger.notice("Deleting information from Vespa and Postgres")
curr_ind_name, sec_ind_name = get_both_index_names(db_session) curr_ind_name, sec_ind_name = get_both_index_names(db_session)
document_index = get_default_document_index( document_index = get_default_document_index(
primary_index_name=curr_ind_name, secondary_index_name=sec_ind_name primary_index_name=curr_ind_name, secondary_index_name=sec_ind_name
@ -195,16 +195,16 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None:
cc_pair=cc_pair, cc_pair=cc_pair,
pair_id=cc_pair_id, pair_id=cc_pair_id,
) )
logger.info(f"Deleted {files_deleted_count} files!") logger.notice(f"Deleted {files_deleted_count} files!")
except Exception as e: except Exception as e:
logger.error(f"Failed to delete connector due to {e}") logger.error(f"Failed to delete connector due to {e}")
if file_names: if file_names:
logger.info("Deleting stored files!") logger.notice("Deleting stored files!")
file_store = get_default_file_store(db_session) file_store = get_default_file_store(db_session)
for file_name in file_names: for file_name in file_names:
logger.info(f"Deleting file {file_name}") logger.notice(f"Deleting file {file_name}")
file_store.delete_file(file_name) file_store.delete_file(file_name)

View File

@ -21,7 +21,7 @@ logger = setup_logger()
def save_postgres(filename: str, container_name: str) -> None: def save_postgres(filename: str, container_name: str) -> None:
logger.info("Attempting to take Postgres snapshot") logger.notice("Attempting to take Postgres snapshot")
cmd = f"docker exec {container_name} pg_dump -U {POSTGRES_USER} -h {POSTGRES_HOST} -p {POSTGRES_PORT} -W -F t {POSTGRES_DB}" cmd = f"docker exec {container_name} pg_dump -U {POSTGRES_USER} -h {POSTGRES_HOST} -p {POSTGRES_PORT} -W -F t {POSTGRES_DB}"
with open(filename, "w") as file: with open(filename, "w") as file:
subprocess.run( subprocess.run(
@ -35,7 +35,7 @@ def save_postgres(filename: str, container_name: str) -> None:
def load_postgres(filename: str, container_name: str) -> None: def load_postgres(filename: str, container_name: str) -> None:
logger.info("Attempting to load Postgres snapshot") logger.notice("Attempting to load Postgres snapshot")
try: try:
alembic_cfg = Config("alembic.ini") alembic_cfg = Config("alembic.ini")
command.upgrade(alembic_cfg, "head") command.upgrade(alembic_cfg, "head")
@ -57,7 +57,7 @@ def load_postgres(filename: str, container_name: str) -> None:
def save_vespa(filename: str) -> None: def save_vespa(filename: str) -> None:
logger.info("Attempting to take Vespa snapshot") logger.notice("Attempting to take Vespa snapshot")
continuation = "" continuation = ""
params = {} params = {}
doc_jsons: list[dict] = [] doc_jsons: list[dict] = []

View File

@ -51,4 +51,4 @@ LOG_FILE_NAME = os.environ.get("LOG_FILE_NAME") or "danswer"
# Enable generating persistent log files for local dev environments # Enable generating persistent log files for local dev environments
DEV_LOGGING_ENABLED = os.environ.get("DEV_LOGGING_ENABLED", "").lower() == "true" DEV_LOGGING_ENABLED = os.environ.get("DEV_LOGGING_ENABLED", "").lower() == "true"
# notset, debug, info, notice, warning, error, or critical # notset, debug, info, notice, warning, error, or critical
LOG_LEVEL = os.environ.get("LOG_LEVEL", "info") LOG_LEVEL = os.environ.get("LOG_LEVEL", "notice")