Newrelic 是APM(Application Performance Management)(應用性能管理/監控)解決方案提供商。項目中,一般用它來追蹤應用的性能。最近看了一下 newrelic-python-agent 源碼,這是查看源碼過程當中的一些記錄。
newrelic 目錄結構以下:html
newrelic ├── admin # 經常使用命令 ├── api # 探針 ├── bootstrap ├── common ├── core ├── extras │ └── framework_django │ └── templatetags ├── hooks # 數據庫 web 各個庫的一些探針 │ ├── framework_tornado │ ├── framework_tornado_r3 │ └── framework_tornado_r4 ├── network ├── packages │ ├── requests │ │ └── packages │ │ ├── chardet │ │ └── urllib3 │ │ ├── packages │ │ │ └── ssl_match_hostname │ │ └── util │ └── wrapt └── samplers
使用 newrelic-admin help
能夠列出全部命令:python
$ newrelic-admin help Usage: newrelic-admin command [options] Type 'newrelic-admin help <command>'for help on a specific command. Available commands are: generate-config license-info license-key local-config network-config record-deploy run-program run-python server-config validate-config
經過 setup.py 代碼能夠知道:web
if with_setuptools: kwargs['entry_points'] = { 'console_scripts': ['newrelic-admin = newrelic.admin:main'], }
newrelic-admin
命令調用的是 newrelic.admin:main
,這是代碼的入口。首先看一下 newrelic/admin/
目錄。數據庫
admin 目錄是 newrelic-admin help 列出的命令腳本所在目錄。
包含文件以下:django
$ tree admin ├── __init__.py ├── __main__.py ├── debug_console.py ├── generate_config.py ├── license_info.py ├── license_key.py ├── local_config.py ├── network_config.py ├── record_deploy.py ├── run_program.py ├── run_python.py ├── server_config.py └── validate_config.py
__init__.py
的 main 函數 是命令執行的入口。flask
__init__.py
文件中代碼bootstrap
load_internal_plugins() load_external_plugins()
用來加載 _builtin_plugins
中定義的命令。小程序
首先看下 run_program 命令,這個命令使用方式以下:設計模式
newrelic-admin run-program your command
newrelic/admin/run_program.py
中 run_program
函數有裝飾器 command,用來定義將命令以及相關說明添加到字典 _commands
。api
在 run_program
中代碼:
root_directory = os.path.dirname(root_directory) boot_directory = os.path.join(root_directory, 'bootstrap') if 'PYTHONPATH' in os.environ: path = os.environ['PYTHONPATH'].split(os.path.pathsep) if not boot_directory in path: python_path = "%s%s%s" % (boot_directory, os.path.pathsep, os.environ['PYTHONPATH']) os.environ['PYTHONPATH'] = python_path
能夠發現newrelic/bootstrap/sitecustomize.py
文件被加入到了 PYTHONPATH。
python 解釋器初始化的時候會自動 importPYTHONPATH
下存在的sitecustomize
和usercustomize
模塊。
以後的功能比較簡單,就是調用 os 模塊執行命令。
如今看下newrelic/bootstrap/sitecustomize.py
代碼。
在 這個文件的最後一行:
newrelic.config.initialize(config_file, environment)
這裏用來初始化newrelic,具體代碼在 newrelic/config.py
文件。
如下是initialize函數:
def initialize(config_file=None, environment=None, ignore_errors=None, log_file=None, log_level=None): if config_file is None: config_file = os.environ.get('NEW_RELIC_CONFIG_FILE', None) if environment is None: environment = os.environ.get('NEW_RELIC_ENVIRONMENT', None) if ignore_errors is None: ignore_errors = newrelic.core.config._environ_as_bool( 'NEW_RELIC_IGNORE_STARTUP_ERRORS', True) _load_configuration(config_file, environment, ignore_errors, log_file, log_level) # 加載配置 if _settings.monitor_mode or _settings.developer_mode: _settings.enabled = True _setup_instrumentation() # 設置探針 _setup_data_source() # TODO _setup_extensions() # TODO _setup_agent_console() # TODO else: _settings.enabled = False
其中第14行 _load_configuration
是用來加載 newrelic 的相關配置。好比:日誌目錄、各類環境變量、祕鑰、newrelic host 地址等等。
`_setup_instrumentation() 中 _process_module_builtin() 用來設置探針。
數據庫、外部請求 等監控模塊都位於 hook 目錄下,經過 _process_module_builtin
函數將進程與監控模塊進行綁定,包括 django 的主要模塊以及經常使用的數據庫等。在覈心模塊執行的時候觸發監控,將數據回傳到 api.time_trace
模塊進行處理。
而對於硬件信息的檢測則由 commo.system_info
進行。
如下爲 flask 應用初始化過程,其它應用相似:
newrelic/admin/__init__.py main()
newrelic/admin/run_program.py
代碼中會把 newrelic/bootstrap/sitecustomize.py
添加到 PYTHONPATH
,python 解釋器初始化的時候會自動 import PYTHONPATH
下存在的 sitecustomize
和 usercustomize
模塊newrelic/bootstrap/sitecustomize.py
調用 newrelic.config.initialize()
,_setup_instrumentation()
函數被調用,_process_module_builtin
會把須要 wrap 的包先添加到_import_hooks。newrelic/config.py
中 sys.meta_path.insert(0, newrelic.api.import_hook.ImportHookFinder())
執行newrelic/api/import_hook.py ImportHookFinder().find_model()
newrelic/api/import_hook.py _ImportHookLoader() or _ImportHookChainedLoader()
newrelic/api/import_hook.py _notify_import_hooks
callable
爲 newrelic/config _module_import_hook _instrument
newrelic/hooks/framework_flask.py instrument_flask_app
newrelic/api/web_transaction.py wrap_wsgi_application
newrelic/common/object_wrapper.py wrap_object
在代碼中,使用到了第三方包 wrapt
,如下是 wrapt 的官方描述(文檔地址)。
wrapt模塊的目的是爲Python提供一個透明的對象代理,它能夠做爲構建函數包裝器和裝飾函數的基礎。wrapt 提供了一個簡單易用的decorator工廠,利用它你能夠簡單地建立decorator,而且在任何狀況下均可以正確地使用它們。
wrapt
簡單示例以下:
import wrapt # 普通裝飾器 @wrapt.decorator def pass_through(wrapped, instance, args, kwargs): return wrapped(*args, **kwargs) @pass_through def function(): pass # 帶參數的裝飾器 import wrapt def with_arguments(myarg1, myarg2): @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): return wrapped(*args, **kwargs) return wrapper @with_arguments(1, 2) def function(): pass
要實現decorator,須要首先定義一個裝飾器函數。這將在每次調用修飾函數時調用。裝飾器函數須要使用四個位置參數:
具體使用參考文檔吧。 文檔地址
newrelic 源碼仔細看下去,太...複雜了。下一篇再分析一個 flask 請求到結束探針工做的完整過程吧。
最後,感謝女友支持和包容,比❤️
也能夠在公號輸入如下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程