From b487b286e1bc3a8ad08d02f35f860e070c36b15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= Date: Mon, 18 Aug 2025 00:38:25 +0800 Subject: [PATCH] add NewRelic --- main.py | 21 ++++ newrelic.ini | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + uv.lock | 18 ++++ 4 files changed, 295 insertions(+) create mode 100644 newrelic.ini diff --git a/main.py b/main.py index ac2509c..a42f96e 100644 --- a/main.py +++ b/main.py @@ -29,6 +29,27 @@ from app.service.init_geoip import init_geoip from app.service.osu_rx_statistics import create_rx_statistics from app.service.recalculate import recalculate +# 检查 New Relic 配置文件是否存在,如果存在则初始化 New Relic +newrelic_config_path = os.path.join(os.path.dirname(__file__), "newrelic.ini") +if os.path.exists(newrelic_config_path): + try: + import newrelic.agent + + environment = os.environ.get( + "NEW_RELIC_ENVIRONMENT", + "production" if not settings.debug else "development" + ) + + newrelic.agent.initialize(newrelic_config_path, environment) + logger.info(f"[NewRelic] Enabled, environment: {environment}") + except ImportError: + logger.warning("[NewRelic] Config file found but 'newrelic' package is not installed") + except Exception as e: + logger.error(f"[NewRelic] Initialization failed: {e}") +else: + logger.info("[NewRelic] No newrelic.ini config file found, skipping initialization") + + from fastapi import FastAPI, HTTPException, Request from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware diff --git a/newrelic.ini b/newrelic.ini new file mode 100644 index 0000000..4a3c202 --- /dev/null +++ b/newrelic.ini @@ -0,0 +1,255 @@ +# --------------------------------------------------------------------------- + +# +# 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 may also be set using the NEW_RELIC_LICENSE_KEY +# environment variable. 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 = 142680e050caa393c0aed741f717f1baFFFFNRAL + +# The application name. Set this to be the name of your +# application as you would like it to show up in New Relic UI. +# You may also set this using the NEW_RELIC_APP_NAME environment variable. +# 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 = g0v0-server + +# 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. It may also be set using the +# NEW_RELIC_MONITOR_MODE environment variable. +monitor_mode = true + +# 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. It can also be set using the NEW_RELIC_LOG +# environment variable. +log_file = stdout + +# 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. This may also be set using the +# NEW_RELIC_LOG_LEVEL environment variable. +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. It can +# also be set using the NEW_RELIC_HIGH_SECURITY environment variable. +# 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. +# See https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/#proxy +# for information on proxy configuration via environment variables. +# 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. +# 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'. +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 = + +# 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 = true + +# 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. +# This can also be set using the NEW_RELIC_API_KEY +# environment variable. +# 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 + +# This setting enables log decoration, the forwarding of log events, +# and the collection of logging metrics if these sub-feature +# configurations are also enabled. If this setting is false, no +# logging instrumentation features are enabled. This can also be +# set using the NEW_RELIC_APPLICATION_LOGGING_ENABLED environment +# variable. +# application_logging.enabled = true + +# If true, the agent captures log records emitted by your application +# and forwards them to New Relic. `application_logging.enabled` must +# also be true for this setting to take effect. You can also set +# this using the NEW_RELIC_APPLICATION_LOGGING_FORWARDING_ENABLED +# environment variable. +# application_logging.forwarding.enabled = true + +# If true, the agent decorates logs with metadata to link to entities, +# hosts, traces, and spans. `application_logging.enabled` must also +# be true for this setting to take effect. This can also be set +# using the NEW_RELIC_APPLICATION_LOGGING_LOCAL_DECORATING_ENABLED +# environment variable. +# application_logging.local_decorating.enabled = true + +# If true, the agent captures metrics related to the log lines +# being sent up by your application. This can also be set +# using the NEW_RELIC_APPLICATION_LOGGING_METRICS_ENABLED +# environment variable. +# application_logging.metrics.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 +# "development", "test", "staging" or "production". +# + +[newrelic:development] +monitor_mode = false + +[newrelic:test] +monitor_mode = false + +[newrelic:staging] +app_name = g0v0-server (Staging) +monitor_mode = true + +[newrelic:production] +monitor_mode = true + +# --------------------------------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 5224ac7..7ef93e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ "loguru>=0.7.3", "maxminddb>=2.8.2", "msgpack-lazer-api", + "newrelic>=10.1.0", "osupyparser>=1.0.7", "passlib[bcrypt]>=1.7.4", "pillow>=11.3.0", diff --git a/uv.lock b/uv.lock index f21ae87..ac18584 100644 --- a/uv.lock +++ b/uv.lock @@ -545,6 +545,7 @@ dependencies = [ { name = "loguru" }, { name = "maxminddb" }, { name = "msgpack-lazer-api" }, + { name = "newrelic" }, { name = "osupyparser" }, { name = "passlib", extra = ["bcrypt"] }, { name = "pillow" }, @@ -583,6 +584,7 @@ requires-dist = [ { name = "loguru", specifier = ">=0.7.3" }, { name = "maxminddb", specifier = ">=2.8.2" }, { name = "msgpack-lazer-api", editable = "packages/msgpack_lazer_api" }, + { name = "newrelic", specifier = ">=10.1.0" }, { name = "osupyparser", git = "https://github.com/MingxuanGame/osupyparser.git" }, { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.4" }, { name = "pillow", specifier = ">=11.3.0" }, @@ -931,6 +933,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" }, ] +[[package]] +name = "newrelic" +version = "10.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/71/07c41fd5e8c94e95216b000d00c3cf735f769cb406c0f33c6ff83b7b7418/newrelic-10.16.0.tar.gz", hash = "sha256:d20eb934380a88d787f93e037d2ccfd5a7c80e657db5bb2e645216eaafe32e26", size = 1267210, upload-time = "2025-08-14T22:23:47.619Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/4b/ba14854270412686fdc331b1503b90554968e58d504297572bfc9cdaf3ad/newrelic-10.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f74301ed32ce625fb84c8bd0c079498dca603ff111d5baa2f59b6480ef7355b", size = 858772, upload-time = "2025-08-14T22:23:19.058Z" }, + { url = "https://files.pythonhosted.org/packages/97/41/de8f0be9e285371cee22a8399840141ffb34ffa5462d31c80c5d7a951ed5/newrelic-10.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3efef3b10f02a086d9009a80b020db8f0b1db5072075779c9facd5d2fe3bb916", size = 858430, upload-time = "2025-08-14T22:23:20.658Z" }, + { url = "https://files.pythonhosted.org/packages/52/74/2fc1e2d96029b57397eb3e2fae9f5e5840af3b98e896834b7a584f614b98/newrelic-10.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8328f621f3683b28156cfa8e1648a56b894480bf4c4953cde7e659b1480ae1e9", size = 856401, upload-time = "2025-08-14T22:23:21.981Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/fb73714fbf105b0c96faea68d17149dfecaa0209a6354b6a6d67c77ef65f/newrelic-10.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07f65faed0a9aa40695e6e6ce4c943ca9c9643fc848c2344bb26665a65893a80", size = 856270, upload-time = "2025-08-14T22:23:23.407Z" }, + { url = "https://files.pythonhosted.org/packages/76/6d/47389d4a9390af2834f552d3bb7ed9b7c9ded30054409621e941ddbe1083/newrelic-10.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:053e73518cf0b2af412490723d3fa8338dab6ed48b748c0b7a0288160d1c33e2", size = 858759, upload-time = "2025-08-14T22:23:24.957Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3e/9e44b713450c89d9d9b0a2626059014ad6e6699992fd687aec4cacb54b35/newrelic-10.16.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8263ae16eb4745def12bace2e3e38e013e76d4e765333a77342b270702e3eaa9", size = 858435, upload-time = "2025-08-14T22:23:26.536Z" }, + { url = "https://files.pythonhosted.org/packages/61/a6/660dfd715011e2fef4d8e5f91c8c040f4ac125bc3bdc644ae0e514cbe87c/newrelic-10.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e83e1341a72ac92118d1af40c2c3ba56069c589acceb1e89d236beab8d7891a", size = 856574, upload-time = "2025-08-14T22:23:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/59/9a/25935a1b999cef132a1597ca3686c028f5472e58d26bfe33876e8af38f4f/newrelic-10.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71950c5e1f26bf1ed563d3641da97f6bb209f80697675888ee12808d8ac69452", size = 856448, upload-time = "2025-08-14T22:23:29.483Z" }, +] + [[package]] name = "nodeenv" version = "1.9.1"