diff --git a/.dockleconfig b/.dockleconfig index e3348182b..3e76ceb8e 100644 --- a/.dockleconfig +++ b/.dockleconfig @@ -3,4 +3,4 @@ # DOCKLE_ACCEPT_FILES="file1,path/to/file2,file3/path,etc" # https://github.com/goodwithtech/dockle#accept-suspicious-environment-variables--files--file-extensions # The apiflask/settings file is a stub file that apiflask creates, and has no sensitive data in. We are ignoring it since it is unused -DOCKLE_ACCEPT_FILES=api/.venv/lib/python3.13/site-packages/apiflask/settings.py,analytics/.venv/lib/python3.13/site-packages/jedi/settings.py +DOCKLE_ACCEPT_FILES=api/.venv/lib/python3.13/site-packages/newrelic/api/settings.py,api/.venv/lib/python3.13/site-packages/apiflask/settings.py,analytics/.venv/lib/python3.13/site-packages/jedi/settings.py diff --git a/analytics/src/analytics/logs/ecs_background_task.py b/analytics/src/analytics/logs/ecs_background_task.py index 4f398093d..85de51c79 100644 --- a/analytics/src/analytics/logs/ecs_background_task.py +++ b/analytics/src/analytics/logs/ecs_background_task.py @@ -63,8 +63,6 @@ def _ecs_background_task_impl(task_name: str) -> Generator[None, None, None]: start = time.perf_counter() _add_log_metadata(task_name) - # initialize new relic here when we add that - logger.info("Starting ECS task %s", task_name) try: diff --git a/api/newrelic.ini b/api/newrelic.ini new file mode 100644 index 000000000..dc2b5f468 --- /dev/null +++ b/api/newrelic.ini @@ -0,0 +1,270 @@ +# --------------------------------------------------------------------------- + +# +# This file configures the New Relic Python Agent. +# +# The path to the configuration file should be supplied to the function +# newrelic.agent.initialize() when the agent is being initialized. +# +# The configuration file follows a structure similar to what you would +# find for Microsoft Windows INI files. For further information on the +# configuration file format see the Python ConfigParser documentation at: +# +# http://docs.python.org/library/configparser.html +# +# For further discussion on the behaviour of the Python agent that can +# be configured via this configuration file see: +# +# https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/ +# + +# --------------------------------------------------------------------------- + +# Here are the settings that are common to all environments. + +[newrelic] + +# You must specify the license key associated with your New +# Relic account. This key binds the Python Agent's data to your +# account in the New Relic service. For more information on +# storing and generating license keys, see +# https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key +# license_key = # Supplied through NEW_RELIC_LICENSE_KEY by AWS SSM. + +# The application name. Set this to be the name of your +# application as you would like it to show up in New Relic UI. +# The UI will then auto-map instances of your application into a +# entry on your home dashboard page. You can also specify multiple +# app names to group your aggregated data. For further details, +# please see: +# https://docs.newrelic.com/docs/apm/agents/manage-apm-agents/app-naming/use-multiple-names-app/ +# app_name = # Parameterized by environment at the end of this file. + +# When "true", the agent collects performance data about your +# application and reports this data to the New Relic UI at +# newrelic.com. This global switch is normally overridden for +# each environment below. +monitor_mode = false + +# Sets the name of a file to log agent messages to. Whatever you +# set this to, you must ensure that the permissions for the +# containing directory and the file itself are correct, and +# that the user that your web application runs as can write out +# to the file. If not able to out a log file, it is also +# possible to say "stderr" and output to standard error output. +# This would normally result in output appearing in your web +# server log. +# TODO: Figure out where (in CloudWatch or Splunk) New Relic agent messages may need to be sent. +log_file = stderr + +# Sets the level of detail of messages sent to the log file, if +# a log file location has been provided. Possible values, in +# increasing order of detail, are: "critical", "error", "warning", +# "info" and "debug". When reporting any agent issues to New +# Relic technical support, the most useful setting for the +# support engineers is "debug". However, this can generate a lot +# of information very quickly, so it is best not to keep the +# agent at this level for longer than it takes to reproduce the +# problem you are experiencing. +log_level = info + +# High Security Mode enforces certain security settings, and prevents +# them from being overridden, so that no sensitive data is sent to New +# Relic. Enabling High Security Mode means that request parameters are +# not collected and SQL can not be sent to New Relic in its raw form. +# To activate High Security Mode, it must be set to 'true' in this +# local .ini configuration file AND be set to 'true' in the +# server-side configuration in the New Relic user interface. For +# details, see +# https://docs.newrelic.com/docs/subscriptions/high-security +high_security = false + +# The Python Agent will attempt to connect directly to the New +# Relic service. If there is an intermediate firewall between +# your host and the New Relic service that requires you to use a +# HTTP proxy, then you should set both the "proxy_host" and +# "proxy_port" settings to the required values for the HTTP +# proxy. The "proxy_user" and "proxy_pass" settings should +# additionally be set if proxy authentication is implemented by +# the HTTP proxy. The "proxy_scheme" setting dictates what +# protocol scheme is used in talking to the HTTP proxy. This +# would normally always be set as "http" which will result in the +# agent then using a SSL tunnel through the HTTP proxy for end to +# end encryption. +# proxy_scheme = http +# proxy_host = hostname +# proxy_port = 8080 +# proxy_user = +# proxy_pass = + +# Capturing request parameters is off by default. To enable the +# capturing of request parameters, first ensure that the setting +# "attributes.enabled" is set to "true" (the default value), and +# then add "request.parameters.*" to the "attributes.include" +# setting. For details about attributes configuration, please +# consult the documentation. +# TODO: Figure out if add'l attrs will be important to capture (e.g. in events or transaction traces) later on. +# attributes.enabled = true +# attributes.include = request.parameters.* + +# The transaction tracer captures deep information about slow +# transactions and sends this to the UI on a periodic basis. The +# transaction tracer is enabled by default. Set this to "false" +# to turn it off. +transaction_tracer.enabled = true + +# Threshold in seconds for when to collect a transaction trace. +# When the response time of a controller action exceeds this +# threshold, a transaction trace will be recorded and sent to +# the UI. Valid values are any positive float value, or (default) +# "apdex_f", which will use the threshold for a dissatisfying +# Apdex controller action - four times the Apdex T value. +transaction_tracer.transaction_threshold = apdex_f + +# When the transaction tracer is on, SQL statements can +# optionally be recorded. The recorder has three modes, "off" +# which sends no SQL, "raw" which sends the SQL statement in its +# original form, and "obfuscated", which strips out numeric and +# string literals. +transaction_tracer.record_sql = obfuscated + +# Threshold in seconds for when to collect stack trace for a SQL +# call. In other words, when SQL statements exceed this +# threshold, then capture and send to the UI the current stack +# trace. This is helpful for pinpointing where long SQL calls +# originate from in an application. +transaction_tracer.stack_trace_threshold = 0.5 + +# Determines whether the agent will capture query plans for slow +# SQL queries. Only supported in MySQL and PostgreSQL. Set this +# to "false" to turn it off. +transaction_tracer.explain_enabled = true + +# Threshold for query execution time below which query plans +# will not not be captured. Relevant only when "explain_enabled" +# is true. +transaction_tracer.explain_threshold = 0.5 + +# Space separated list of function or method names in form +# 'module:function' or 'module:class.function' for which +# additional function timing instrumentation will be added. +transaction_tracer.function_trace = + +# The error collector captures information about uncaught +# exceptions or logged exceptions and sends them to UI for +# viewing. The error collector is enabled by default. Set this +# to "false" to turn it off. For more details on errors, see +# https://docs.newrelic.com/docs/apm/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/ +error_collector.enabled = true + +# To stop specific errors from reporting to the UI, set this to +# a space separated list of the Python exception type names to +# ignore. The exception name should be of the form 'module:class'. +# +# Explicitly not on the list for now: +# - pydantic.error_wrappers:ValidationError (These seem like real coding issues) +# +# Note that most of these except for UnsupportedMediaTypeProblem are 400 responses. +# +error_collector.ignore_classes = + +# Expected errors are reported to the UI but will not affect the +# Apdex or error rate. To mark specific errors as expected, set this +# to a space separated list of the Python exception type names to +# expected. The exception name should be of the form 'module:class'. +error_collector.expected_classes = + +# Status codes ignored by default: 100-102 200-208 226 300-308 404 + +# Addtional status codes to ignore reporting: +# 401: Unauthorized - Invalid or missing JWT +# 402: Payment Required - Employer does not have withholding data +# 403: Forbidden - User does not have access to endpoint or resource +# 405: Method Not Allowed +# 406: Not Acceptable - Invalid Accept header (API does not support it) +# 415: Unsupported Media Type - Invalid media types for upload +# 503: Service Unavailable - Temporary service unavailability. High volume should trigger a New Relic alarm. +# +# Status codes that we may want to ignore in the future: +# +# - 400: Bad Request - Validation and extra parameter errors. Unclear if we want to catch pydantic ValidationErrors here so we're keeping them for now. Instead, selectively ignore specific error classes above. +# - 422: Unprocessable Entity - Haven't seen these yet so we'll report them. +# - 504: Gateway Timeout - We do not expect any 504s to be thrown from the API server itself. This should come from the API Gateway that sits in front of it. +# +error_collector.ignore_status_codes = 401 402 403 404 405 406 415 503 + +# Browser monitoring is the Real User Monitoring feature of the UI. +# For those Python web frameworks that are supported, this +# setting enables the auto-insertion of the browser monitoring +# JavaScript fragments. +browser_monitoring.auto_instrument = false + +# A thread profiling session can be scheduled via the UI when +# this option is enabled. The thread profiler will periodically +# capture a snapshot of the call stack for each active thread in +# the application to construct a statistically representative +# call tree. For more details on the thread profiler tool, see +# https://docs.newrelic.com/docs/apm/apm-ui-pages/events/thread-profiler-tool/ +thread_profiler.enabled = true + +# Your application deployments can be recorded through the +# New Relic REST API. To use this feature provide your API key +# below then use the `newrelic-admin record-deploy` command. +# api_key = + +# Distributed tracing lets you see the path that a request takes +# through your distributed system. For more information, please +# consult our distributed tracing planning guide. +# https://docs.newrelic.com/docs/transition-guide-distributed-tracing +distributed_tracing.enabled = true + +# When storing errors in database, distributed tracing solution captures the database query +# and sends the full, unscrubbed message to New Relic. This enablement will ensure that +# no PII data is captured in messages of new relic. +strip_exception_messages.enabled = true + +# --------------------------------------------------------------------------- + +# +# The application environments. These are specific settings which +# override the common environment settings. The settings related to a +# specific environment will be used when the environment argument to the +# newrelic.agent.initialize() function has been defined to be either +# "local", "stage", "performance", "training", "prod", or "uat". +# + +[newrelic:local] +# Don't turn on data reporting by default when running the API locally. +# +# To enable New Relic locally, set the following variables: +# - developer mode: false +# - monitor_mode: true +# - license_key: retrieved from New Relic here: https://one.newrelic.com/launcher/api-keys-ui.launcher +# +# NOTE: DO NOT COMMIT THE LICENSE KEY IN A GIT COMMIT. +# +# Less scary note: do not wrap your license key in quotes, it should look like this: +# license_key=1234abcd +# +app_name = SIMPLER-GRANTS-API-LOCAL +developer_mode = true +monitor_mode = false +license_key=replace_me + +application_logging.enabled = false +application_logging.forwarding.enabled = false +application_logging.local_decorating.enabled = false + +[newrelic:staging] +app_name = SIMPLER-GRANTS-API-STAGING +monitor_mode = true + +[newrelic:prod] +app_name = SIMPLER-GRANTS-API-PROD +monitor_mode = true + +[newrelic:dev] +app_name = SIMPLER-GRANTS-API-DEV +monitor_mode = true + +# --------------------------------------------------------------------------- diff --git a/api/poetry.lock b/api/poetry.lock index 6cd21ef0f..48f89a2ad 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1128,6 +1128,47 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "newrelic" +version = "10.3.1" +description = "New Relic Python Agent" +optional = false +python-versions = ">=3.7" +files = [ + {file = "newrelic-10.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:212fddfae053bd0ee803d17b8cb91b75360f416566d81382046e88a2d3159b75"}, + {file = "newrelic-10.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba92c1d480d7b0ec457e7ab059cf2b202a366300ee382b9efa5f94bf0d646745"}, + {file = "newrelic-10.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6d174cb16054ddc647e12ce721e7174806de0552cc3b288d798245a65e4c85b3"}, + {file = "newrelic-10.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:203b531bf7e1625b64229ad88cc0588f207a4b5116ba4d1606980447d7f04cc4"}, + {file = "newrelic-10.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:189382cb73f790584ed5885632873c27857844bd895f50ca12504bebf441bdd1"}, + {file = "newrelic-10.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0161152fad3c6a16d7ee1aee4fedb99df760ddf2b37bf84f53ddf0b2349cde7a"}, + {file = "newrelic-10.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c412fa0fd04332fb2016769a53f89eaed9834b0b205c99bb39dfbb06707f5517"}, + {file = "newrelic-10.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5cd69a0fcc449c0844103832886cda76b9c8244e77b9e77c4f1c2c4088c1b9fc"}, + {file = "newrelic-10.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b0362437217735e379b5af13056d3b42c62fec3b815747c3d324b8dc29a23a1"}, + {file = "newrelic-10.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94de1f405ee2ac7752f64c1620dc7921f5e73b7d1adda0fd1386b24d42fb1e71"}, + {file = "newrelic-10.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84d167bcc3f473cb4ab8c20516f442922837d7b9d896f4c419bdb9674dbf1401"}, + {file = "newrelic-10.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:649656b4bc07eef4d891ed9a01ac2116ab009a6106c4ce146d2ae83f597a1339"}, + {file = "newrelic-10.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92b55999c800d1b9c6e390e1ed435003d39905afd78feec6cedd48dc0401b05"}, + {file = "newrelic-10.3.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4028726e651868f349985fe5ae4a4dc7e9ede6aa6e292e5f4d658e18e6956ac"}, + {file = "newrelic-10.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07146f8dc884a59ea7ef32fd095075449199372d37df821cb73b033e4ff85598"}, + {file = "newrelic-10.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:378309f2713b0f8e2cb17506d4e0507731cbb41de9126ccee4e4a8b91832cb98"}, + {file = "newrelic-10.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd75831833330d331edc60fc79104ac2e9afd32ffc75bcca9b27813c5096905"}, + {file = "newrelic-10.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aff06ae5c8ff0938ed75c4e62bffe2bfd2400f40ec2ed1867c57055cb0cb64f"}, + {file = "newrelic-10.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:075e5bb8d2830449aa24935522b778c336da910d7e5b0c41b97f1fd9f7e974df"}, + {file = "newrelic-10.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d9eaadbdbc44aeae5247d49be030ecbd5093cf0482766c80a1748979f1fb6931"}, + {file = "newrelic-10.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a49990b780b65a9ea5ef953b5abd2fa56434c0051413eb246b827fda573159c"}, + {file = "newrelic-10.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891eceefaa438063d1f139dfaeb9837ffcc7a4ada84ea96ea681d529170c5d0c"}, + {file = "newrelic-10.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17097aa4d62304fbec37a9df32a0ef1cc393bc6ac6581a088aefc686547403dd"}, + {file = "newrelic-10.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d6c849100f0d7259e35f1d882a3e322878913c250fe79960d1d3adfe69e358a6"}, + {file = "newrelic-10.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38273663a462deb97f7ebe1320fe46ef479067d3d9dbc205c8be53dc2a4ee0dc"}, + {file = "newrelic-10.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8015e62a83d142aa0212783c58457005a33193f809c3e03fbcab922f8b5c8c0c"}, + {file = "newrelic-10.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c631eadc61a3a5202cb9bcaf2094d580b50104d82e04abe30be93a318849bc28"}, + {file = "newrelic-10.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e6c26a38e20de4c0ec2c423f6fd3101915c207dff7cefbb85378f1d57d26f30"}, + {file = "newrelic-10.3.1.tar.gz", hash = "sha256:203573e485b36d6f2deb91e974a80a97b70a798b91751480f43f24193c3848c8"}, +] + +[package.extras] +infinite-tracing = ["grpcio", "protobuf"] + [[package]] name = "opensearch-py" version = "2.8.0" diff --git a/api/pyproject.toml b/api/pyproject.toml index 95b2552e9..3e2b0c5b2 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -4,6 +4,7 @@ version = "0.1.0" description = "Back end API for simpler.grants.gov" packages = [{ include = "src" }] authors = ["Nava Engineering "] +include = ["newrelic.ini"] [tool.poetry.dependencies] # See /documentation/api/package-depedency-management.md#Upgrading Python @@ -26,6 +27,7 @@ pydantic-settings = "^2.0.3" flask-cors = "^5.0.0" opensearch-py = "^2.5.0" pyjwt = "^2.9.0" +newrelic = "10.3.1" [tool.poetry.group.dev.dependencies] black = "^24.0.0" diff --git a/api/src/adapters/newrelic/__init__.py b/api/src/adapters/newrelic/__init__.py new file mode 100644 index 000000000..7be8347ea --- /dev/null +++ b/api/src/adapters/newrelic/__init__.py @@ -0,0 +1,14 @@ +import logging +import os + +import newrelic.agent + +logger = logging.getLogger(__name__) + + +def init_newrelic() -> None: + logger.info("Initializing New Relic") + newrelic.agent.initialize( + config_file=os.path.join(os.path.dirname(__file__), "../../..", "newrelic.ini"), + environment=os.environ.get("ENVIRONMENT", "local"), + ) diff --git a/api/src/adapters/newrelic/events.py b/api/src/adapters/newrelic/events.py new file mode 100644 index 000000000..a399fa4be --- /dev/null +++ b/api/src/adapters/newrelic/events.py @@ -0,0 +1,70 @@ +import logging +import sys +from types import TracebackType +from typing import Any + +import flask +import newrelic.agent + +logger = logging.getLogger(__name__) + +AGENT_ATTRIBUTE_LIMIT = 128 +EVENT_API_ATTRIBUTE_LIMIT = 255 + + +def record_custom_event(event_type: str, params: dict[Any, Any]) -> None: + params["api.request.path"] = params.get("request.path") + params["api.request.method"] = params.get("request.method") + params["api.request.url_rule"] = params.get("request.url_rule") + params["api.request.id"] = params.get("request.id") + + if flask.has_request_context(): + params["api.request.internal_id"] = getattr(flask.g, "internal_request_id", None) + + # If there are more custom attributes than the limit, the agent will upload + # a partial payload, dropping keys after hitting its limit + attribute_count = len(params) + if attribute_count > AGENT_ATTRIBUTE_LIMIT: + logger.warning( + "Payload exceeds New Relic Agent event attribute limit. Partial data likely present. Check CloudWatch for complete event data.", + extra={ + "event_type": event_type, + "total_attribute_count": attribute_count, + }, + ) + logger.info(event_type, extra=params) + + # https://docs.newrelic.com/docs/apm/agents/python-agent/python-agent-api/recordcustomevent-python-agent-api/ + newrelic.agent.record_custom_event( + event_type, + params, + ) + + +def log_and_capture_exception(msg: str, extra: dict | None = None, only_warn: bool = False) -> None: + """ + Sometimes we want to record custom messages that do not match an exception message. For example, when a ValidationError contains + multiple issues, we want to log and record each one individually in New Relic. Injecting a new exception with a + human-readable error message is the only way for errors to receive custom messages. + This does not affect the traceback or any other visible attribute in New Relic. Everything else, including + the original exception class name, is retained and displayed. + """ + + info = sys.exc_info() + info_with_readable_msg: BaseException | tuple[type, BaseException, TracebackType | None] + + if info[0] is None: + exc = Exception(msg) + info_with_readable_msg = (type(exc), exc, exc.__traceback__) + else: + info_with_readable_msg = (info[0], Exception(msg), info[2]) + + if only_warn: + logger.warning(msg, extra=extra, exc_info=info_with_readable_msg) + else: + logger.error(msg, extra=extra, exc_info=info_with_readable_msg) + + newrelic.agent.notice_error( + error=info_with_readable_msg, + attributes=extra, + ) diff --git a/api/src/app.py b/api/src/app.py index 9a78e19e1..0f5552068 100644 --- a/api/src/app.py +++ b/api/src/app.py @@ -13,6 +13,7 @@ import src.api.feature_flags.feature_flag_config as feature_flag_config import src.logging import src.logging.flask_logger as flask_logger +from src.adapters.newrelic import init_newrelic from src.api.agencies_v1 import agency_blueprint as agencies_v1_blueprint from src.api.extracts_v1 import extract_blueprint as extracts_v1_blueprint from src.api.healthcheck import healthcheck_blueprint @@ -51,6 +52,7 @@ def create_app() -> APIFlask: app = APIFlask(__name__, title=TITLE, version=API_OVERALL_VERSION) setup_logging(app) + init_newrelic() register_db_client(app) feature_flag_config.initialize() diff --git a/api/src/task/ecs_background_task.py b/api/src/task/ecs_background_task.py index 3b7fa2a66..7b277d7f4 100644 --- a/api/src/task/ecs_background_task.py +++ b/api/src/task/ecs_background_task.py @@ -63,8 +63,6 @@ def _ecs_background_task_impl(task_name: str) -> Generator[None, None, None]: start = time.perf_counter() _add_log_metadata(task_name) - # initialize new relic here when we add that - logger.info("Starting ECS task %s", task_name) try: