From 55dd7cefe7733e7e44f8eb52441d7867daab38a7 Mon Sep 17 00:00:00 2001 From: eeliu <27064129+eeliu@users.noreply.github.com> Date: Thu, 9 Nov 2023 12:26:57 +0800 Subject: [PATCH] build(testapps): playground for (#548) - add testapps-fastapi - redis/httpx/mysql - change plugin style close https://github.com/pinpoint-apm/pinpoint-c-agent/issues/547 --- .devcontainer/Dockerfile | 3 +- .gitignore | 1 + .gitmodules | 3 + README.md | 29 ++++ collector-agent/agent/AgentRouter.go | 2 +- collector-agent/agent/AgentRouter_test.go | 3 +- collector-agent/agent/SpanSender.go | 2 +- collector-agent/start-collector-agent.sh | 2 +- plugins/PY/pinpointPy/Common.py | 43 ++++-- plugins/PY/pinpointPy/CommonPlugin.py | 16 +- plugins/PY/pinpointPy/Fastapi/AsyCommon.py | 57 +++---- .../PY/pinpointPy/Fastapi/AsyCommonPlugin.py | 4 +- .../PY/pinpointPy/Fastapi/AsyRequestPlugin.py | 4 +- .../Fastapi/FastAPIRequestPlugin.py | 18 ++- .../PY/pinpointPy/Fastapi/PinTranscation.py | 4 +- .../Fastapi/_MotorMongo/__init__.py | 5 +- ...omandPlugins.py => motorCommandPlugins.py} | 54 ++++--- plugins/PY/pinpointPy/Fastapi/__init__.py | 12 +- .../Fastapi/_aioredis/AioRedisPlugins.py | 22 +-- .../pinpointPy/Fastapi/_aioredis/__init__.py | 10 +- .../PY/pinpointPy/Fastapi/_httpx/__init__.py | 5 +- .../pinpointPy/Fastapi/_httpx/httpxPlugins.py | 38 ++--- .../Fastapi/_sqlalchemy/__init__.py | 44 ++++++ .../Fastapi/_sqlalchemy/sqlalchemyPlugin.py | 75 +++++++++ plugins/PY/pinpointPy/WSGIPlugin.py | 3 +- plugins/PY/pinpointPy/__init__.py | 1 - .../PY/pinpointPy/libs/DjangoRest/__init__.py | 20 ++- .../PY/pinpointPy/libs/MySQLdb/__init__.py | 7 +- .../libs/MysqlConnector/__init__.py | 15 +- .../PY/pinpointPy/libs/PyMysql/__init__.py | 16 +- plugins/PY/pinpointPy/libs/__init__.py | 4 +- .../PY/pinpointPy/libs/pyRedis/__init__.py | 14 +- .../PY/pinpointPy/libs/pymongo/__init__.py | 19 ++- .../PY/pinpointPy/libs/requests/__init__.py | 15 +- plugins/PY/pinpointPy/libs/urllib/__init__.py | 17 ++- plugins/PY/pinpointPy/pinpoint.py | 34 +++-- testapps/PY/requirements.txt | 1 - testapps/backend.dockerfile | 15 ++ testapps/backend/__init__.py | 0 testapps/backend/crud.py | 36 +++++ testapps/backend/database.py | 13 ++ testapps/backend/main.py | 135 +++++++++++++++++ testapps/backend/models.py | 26 ++++ testapps/backend/requirements.txt | 9 ++ testapps/backend/schemas.py | 37 +++++ testapps/compose.yaml | 37 +++-- testapps/fastapi.dockerfile | 15 ++ testapps/fastapi/__init__.py | 0 testapps/fastapi/crud.py | 36 +++++ testapps/fastapi/database.py | 13 ++ testapps/fastapi/main.py | 143 ++++++++++++++++++ testapps/fastapi/models.py | 26 ++++ testapps/fastapi/requirements.txt | 9 ++ testapps/fastapi/schemas.py | 37 +++++ testapps/flask.dockerfile | 12 +- testapps/php.dockerfile | 3 +- testapps/test_db | 1 + 57 files changed, 1011 insertions(+), 214 deletions(-) rename plugins/PY/pinpointPy/Fastapi/_MotorMongo/{motorComandPlugins.py => motorCommandPlugins.py} (63%) create mode 100644 plugins/PY/pinpointPy/Fastapi/_sqlalchemy/__init__.py create mode 100644 plugins/PY/pinpointPy/Fastapi/_sqlalchemy/sqlalchemyPlugin.py create mode 100644 testapps/backend.dockerfile create mode 100644 testapps/backend/__init__.py create mode 100644 testapps/backend/crud.py create mode 100644 testapps/backend/database.py create mode 100644 testapps/backend/main.py create mode 100644 testapps/backend/models.py create mode 100644 testapps/backend/requirements.txt create mode 100644 testapps/backend/schemas.py create mode 100644 testapps/fastapi.dockerfile create mode 100644 testapps/fastapi/__init__.py create mode 100644 testapps/fastapi/crud.py create mode 100644 testapps/fastapi/database.py create mode 100644 testapps/fastapi/main.py create mode 100644 testapps/fastapi/models.py create mode 100644 testapps/fastapi/requirements.txt create mode 100644 testapps/fastapi/schemas.py create mode 160000 testapps/test_db diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index de39bd31e..48de22890 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -72,5 +72,6 @@ USER root COPY go1.18.10.linux-amd64.tar.gz /home/pinpoint/go1.18.10.linux-amd64.tar.gz RUN rm -rf /usr/local/go && cd /home/pinpoint/ && tar -C /usr/local -xzf go1.18.10.linux-amd64.tar.gz ENV PATH=$PATH:/usr/local/go/bin -ENV PATH=$PATH:$GO_PATH/bin +ENV +ENV PATH=$PATH:/home/pinpoint/go/bin RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index b25f89631..5570f8bbb 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ testapps/PHP/composer.phar wheelhouse/ .clangd collector-agent/pinpoint-grpc-idl/ +*.so diff --git a/.gitmodules b/.gitmodules index 67b33228f..bcd056d1e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "collector-agent/pinpoint-grpc-idl"] path = collector-agent/pinpoint-grpc-idl url = https://github.com/pinpoint-apm/pinpoint-grpc-idl.git +[submodule "testapps/test_db"] + path = testapps/test_db + url = https://github.com/datacharmer/test_db.git diff --git a/README.md b/README.md index 9a5eb5975..93eab154c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,35 @@ python3|[English](DOC/PY/Readme.md) [中文](DOC/PY/Readme-CN.md) [한국어](DO c/cpp|[English](DOC/C-CPP/Readme.md) golang|[go-aop-agent](https://github.com/pinpoint-apm/go-aop-agent) +#### How to use testapps(playground) + +##### Requirement + +- [ ] [docker compose plugin](https://docs.docker.com/compose/install/linux/) +- [ ] pinpoint platform [Quick-start guide](https://pinpoint-apm.gitbook.io/pinpoint/getting-started/quickstart) +- [ ] map `dev-pinpoint` host to your pinpoint-collector address + ``` + pinpoint@pinpoint:~$ cat /etc/hosts + # 192.168.10.11 is the address of pinpoint-collector + 192.168.10.11 dev-pinpoint + ``` +##### Playground + +``` +$ git clone --recurse-submodules https://github.com/pinpoint-apm/pinpoint-c-agent.git +$ git checkout dev +$ cd pinpoint-c-agent/testapps && docker compose up --build +$ ## testapp-fastapi +$ curl http://localhost:8186/docs#/ +$ ## testapp-php yii2 framework +$ curl http://localhost:8185/index.php +$ ## testapp-flask +$ curl http://localhost:8184/index.php +``` + + + + ## Contact Us * Submit an [issue](https://github.com/pinpoint-apm/pinpoint-c-agent/issues) diff --git a/collector-agent/agent/AgentRouter.go b/collector-agent/agent/AgentRouter.go index 0c8b16a8c..a585f8461 100644 --- a/collector-agent/agent/AgentRouter.go +++ b/collector-agent/agent/AgentRouter.go @@ -73,7 +73,7 @@ type TErrorInfo struct { type TSpan struct { AppServerType int32 `json:"FT"` AppServerTypeV2 int32 `json:":FT"` - ParentAppServerType int `json:"ptype"` + ParentAppServerType int32 `json:"ptype,string"` ParentSpanId int64 `json:"psid,string"` ParentApplicationName string `json:"pname"` StartTime int64 `json:"S"` diff --git a/collector-agent/agent/AgentRouter_test.go b/collector-agent/agent/AgentRouter_test.go index 2256f23f0..4156365d4 100644 --- a/collector-agent/agent/AgentRouter_test.go +++ b/collector-agent/agent/AgentRouter_test.go @@ -30,7 +30,7 @@ func TestGetAgentInfo(t *testing.T) { } func TestTspan(t *testing.T) { - msg := `{"E":1,"FT":1500,":FT":1500,"ptype":1500,"pname":"abc_d","psid":"23563","NP":"t=1617083759.535 D=0.000","S":1617083759798,"appid":"app-2",":appid":"app-2", + msg := `{"E":1,"FT":1500,":FT":1500,"ptype":"1500","pname":"abc_d","psid":"23563","NP":"t=1617083759.535 D=0.000","S":1617083759798,"appid":"app-2",":appid":"app-2", ":appname":"APP-2","appname":"APP-2","calls":[{"E":1,"calls":[{"E":1,"S":0,"clues":["-1:input parameters","14:return value"],"name":"abc"}],"S":0,"clues":["-1:input parameters","14:return value"],"name":"app\\AppDate::abc","SQL":"select* from abc"}],"client":"10.34.135.145","clues":["46:200"],"name":"PHP Request: fpm-fcgi","server":"10.34.130.152:8000","sid":"726125302","stp":"1500","tid":"app-2^1617083747^5506","uri":"/index.php?type=get_date","Ah":"123.35.36.3/host","EXP":"exp","ERR":{"msg":"error_msg","file":"file.cc","line":123}}` var tspan TSpan @@ -79,5 +79,4 @@ func TestTspan(t *testing.T) { } - t.Log(tspan) } diff --git a/collector-agent/agent/SpanSender.go b/collector-agent/agent/SpanSender.go index 97fd11e8d..df7a25550 100644 --- a/collector-agent/agent/SpanSender.go +++ b/collector-agent/agent/SpanSender.go @@ -217,7 +217,7 @@ func (spanSender *SpanSender) makePinpointSpan(span *TSpan) (*v1.PSpan, error) { parentInfo := v1.PParentInfo{ ParentApplicationName: span.ParentApplicationName, - ParentApplicationType: int32(span.ParentAppServerType), + ParentApplicationType: span.ParentAppServerType, AcceptorHost: span.AcceptorHost, } diff --git a/collector-agent/start-collector-agent.sh b/collector-agent/start-collector-agent.sh index 7d37e5317..c8a5250ef 100644 --- a/collector-agent/start-collector-agent.sh +++ b/collector-agent/start-collector-agent.sh @@ -8,7 +8,7 @@ export PP_COLLECTOR_AGENT_STAT_PORT=9992 export PP_COLLECTOR_AGENT_ISDOCKER=false # export PP_LOG_DIR=/tmp/ export PP_Log_Level=DEBUG -export PP_ADDRESS=0.0.0.0@9999 +export PP_ADDRESS=0.0.0.0@10000 export GO_PATH=/home/pinpoint/go/bin export PATH=$PATH:$GO_PATH make && ./collector-agent \ No newline at end of file diff --git a/plugins/PY/pinpointPy/Common.py b/plugins/PY/pinpointPy/Common.py index d927eb77b..294a43707 100644 --- a/plugins/PY/pinpointPy/Common.py +++ b/plugins/PY/pinpointPy/Common.py @@ -19,17 +19,39 @@ # Created by eeliu at 3/5/20 -from pinpointPy import Defines -from pinpointPy import pinpoint +from pinpointPy import Defines, pinpoint, logger from abc import ABCMeta, abstractmethod -class PinTrace(object): - E_PER_REQ = 1 - E_FUNCTION = 2 +class Trace: def __init__(self, name): self.name = name + def onBefore(self, *args, **kwargs): + raise NotImplementedError("onBefore") + + def onEnd(self, ret): + raise NotImplementedError("onEnd") + + def __call__(self, func): + + def pinpointTrace(*args, **kwargs): + try: + args, kwargs = self.onBefore(*args, **kwargs) + ret = func(*args, **kwargs) + except Exception as e: + self.onException(e) + logger.info(f"{func.__name__} catch {e}") + raise e + finally: + return self.onEnd(ret) + return pinpointTrace + + +class PinTrace(Trace): + E_PER_REQ = 1 + E_FUNCTION = 2 + def isSample(self, args): ''' if not root, no trace @@ -42,13 +64,13 @@ def isSample(self, args): def onBefore(self, *args, **kwargs): pinpoint.with_trace() - return args,kwargs + return args, kwargs def onEnd(self, ret): pinpoint.end_trace() def onException(self, e): - raise NotImplementedError() + raise NotImplementedError("onException") def __call__(self, func): self.func_name = func.__name__ @@ -56,14 +78,13 @@ def __call__(self, func): def pinpointTrace(*args, **kwargs): if not self.isSample((args, kwargs)): return func(*args, **kwargs) - - ret = None try: args, kwargs = self.onBefore(*args, **kwargs) ret = func(*args, **kwargs) return ret except Exception as e: self.onException(e) + logger.info(f"{func.__name__} catch {e}") raise e finally: self.onEnd(ret) @@ -72,6 +93,7 @@ def pinpointTrace(*args, **kwargs): def getFuncUniqueName(self): return self.name + class PinHeader: def __init__(self) -> None: # Path field in pinpoint-web @@ -117,7 +139,8 @@ def GetHeader(self,parenthost,parentname,url,...)->PinHeader: @abstractmethod def GetHeader(self, *args, **kwargs) -> PinHeader: return PinHeader() - + + class PinTransaction(PinTrace): def __init__(self, name: str, userGenHeaderCb: GenPinHeader): diff --git a/plugins/PY/pinpointPy/CommonPlugin.py b/plugins/PY/pinpointPy/CommonPlugin.py index 18f44a5b9..4db8f73eb 100644 --- a/plugins/PY/pinpointPy/CommonPlugin.py +++ b/plugins/PY/pinpointPy/CommonPlugin.py @@ -15,24 +15,24 @@ # ------------------------------------------------------------------------------ -from pinpointPy import Common -from pinpointPy import Defines -from pinpointPy import pinpoint +from pinpointPy import Common, Defines, pinpoint class PinpointCommonPlugin(Common.PinTrace): - def onBefore(self,*args, **kwargs): + def onBefore(self, *args, **kwargs): super().onBefore(*args, **kwargs) ############################################################### - pinpoint.add_trace_header(Defines.PP_INTERCEPTOR_NAME, self.getFuncUniqueName()) - pinpoint.add_trace_header(Defines.PP_SERVER_TYPE, Defines.PP_METHOD_CALL) + pinpoint.add_trace_header( + Defines.PP_INTERCEPTOR_NAME, self.getFuncUniqueName()) + pinpoint.add_trace_header( + Defines.PP_SERVER_TYPE, Defines.PP_METHOD_CALL) arg = self.get_arg(*args, **kwargs) pinpoint.add_trace_header_v2(Defines.PP_ARGS, arg) ############################################################### - return args,kwargs + return args, kwargs - def onEnd(self,ret): + def onEnd(self, ret): ############################################################### pinpoint.add_trace_header_v2(Defines.PP_RETURN, str(ret)) ############################################################### diff --git a/plugins/PY/pinpointPy/Fastapi/AsyCommon.py b/plugins/PY/pinpointPy/Fastapi/AsyCommon.py index c8ef1bac9..0671dc22f 100644 --- a/plugins/PY/pinpointPy/Fastapi/AsyCommon.py +++ b/plugins/PY/pinpointPy/Fastapi/AsyCommon.py @@ -16,8 +16,6 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - - import asyncio from starlette_context import context @@ -25,33 +23,37 @@ from pinpointPy import pinpoint -class AsynPinTrace(object): +class TraceContext: + @staticmethod + def get_parent_id(): + id = context.get('_pinpoint_id_', 0) + if id == 0: + return False, None + else: + return True, id + + @staticmethod + def set_parent_id(id: int): + context['_pinpoint_id_'] = id + + +class AsyncPinTrace: def __init__(self, name): self.name = name - def getCurrentId(self): - id = context['_pinpoint_id_'] - if not id: - raise 'not found traceId' - else: - return id - def onBefore(self, parentId, *args, **kwargs): traceId = pinpoint.with_trace(parentId) - # update global id - context['_pinpoint_id_'] = traceId + TraceContext.set_parent_id(traceId) return traceId, args, kwargs @staticmethod def isSample(*args, **kwargs): - try: - parentid = context.get('_pinpoint_id_', 0) - if parentid == 0: - return False, None - return True, parentid - except Exception as e: - return False, None + ret, id = TraceContext.get_parent_id() + if ret: + return True, id, args, kwargs + else: + return False, None, args, kwargs @classmethod def _isSample(cls, *args, **kwargs): @@ -59,7 +61,7 @@ def _isSample(cls, *args, **kwargs): def onEnd(self, parentId, ret): parentId = pinpoint.end_trace(parentId) - context['_pinpoint_id_'] = parentId + TraceContext.set_parent_id(parentId) def onException(self, traceId, e): raise NotImplementedError() @@ -69,20 +71,21 @@ def __call__(self, func): async def pinpointTrace(*args, **kwargs): ret = None - sampled, parentId = self._isSample(args, kwargs) + # avoiding variable missing + # use and return + sampled, parentId, nArgs, nKwargs = self._isSample(*args, **kwargs) if not sampled: - return await func(*args, **kwargs) - - traceId, args, kwargs = self.onBefore(parentId, *args, **kwargs) + return await func(*nArgs, **nKwargs) + traceId, nArgs, nKwargs = self.onBefore( + parentId, *nArgs, **nKwargs) try: - ret = await func(*args, **kwargs) + ret = await func(*nArgs, **nKwargs) return ret except Exception as e: self.onException(traceId, e) raise e finally: self.onEnd(traceId, ret) - return pinpointTrace def getFuncUniqueName(self): @@ -91,7 +94,7 @@ def getFuncUniqueName(self): if __name__ == '__main__': - @AsynPinTrace('main') + @AsyncPinTrace('main') async def run(i): if i == 0: return diff --git a/plugins/PY/pinpointPy/Fastapi/AsyCommonPlugin.py b/plugins/PY/pinpointPy/Fastapi/AsyCommonPlugin.py index 7ade8f38f..4ea063248 100644 --- a/plugins/PY/pinpointPy/Fastapi/AsyCommonPlugin.py +++ b/plugins/PY/pinpointPy/Fastapi/AsyCommonPlugin.py @@ -15,12 +15,12 @@ # ------------------------------------------------------------------------------ -from pinpointPy.Fastapi.AsyCommon import AsynPinTrace +from pinpointPy.Fastapi.AsyCommon import AsyncPinTrace from pinpointPy import Defines from pinpointPy import pinpoint -class CommonPlugin(AsynPinTrace): +class CommonPlugin(AsyncPinTrace): def onBefore(self,parentId, *args, **kwargs): traceId,args,kwargs = super().onBefore(parentId,*args, **kwargs) diff --git a/plugins/PY/pinpointPy/Fastapi/AsyRequestPlugin.py b/plugins/PY/pinpointPy/Fastapi/AsyRequestPlugin.py index 475fc9e41..2ce3729e4 100644 --- a/plugins/PY/pinpointPy/Fastapi/AsyRequestPlugin.py +++ b/plugins/PY/pinpointPy/Fastapi/AsyRequestPlugin.py @@ -18,12 +18,12 @@ # ------------------------------------------------------------------------------ -from pinpointPy.Fastapi.AsyCommon import AsynPinTrace +from pinpointPy.Fastapi.AsyCommon import AsyncPinTrace from pinpointPy import pinpoint from pinpointPy import Defines -class AsyRequestPlugin(AsynPinTrace): +class AsyRequestPlugin(AsyncPinTrace): def __init__(self, name): super().__init__(name) diff --git a/plugins/PY/pinpointPy/Fastapi/FastAPIRequestPlugin.py b/plugins/PY/pinpointPy/Fastapi/FastAPIRequestPlugin.py index 1dee3e44b..d96c4b760 100644 --- a/plugins/PY/pinpointPy/Fastapi/FastAPIRequestPlugin.py +++ b/plugins/PY/pinpointPy/Fastapi/FastAPIRequestPlugin.py @@ -21,6 +21,7 @@ from pinpointPy.Fastapi.AsyRequestPlugin import AsyRequestPlugin from pinpointPy import Defines, pinpoint import sys +from fastapi import Response class FastAPIRequestPlugin(AsyRequestPlugin): @@ -40,11 +41,24 @@ def onBefore(self, parentId, *args, **kwargs): self.request = request return traceId, args, kwargs - def onEnd(self, traceId, response): - ut = self.request.scope['root_path'] + self.request.scope['route'].path + def onEnd(self, traceId, response: Response): + # fix bug in in fastapi/docs + ut = '/' + if 'root_path' in self.request.scope: + ut += self.request.scope['root_path'] + if 'route' in self.request.scope: + ut += self.request.scope['route'].path + pinpoint.add_trace_header(Defines.PP_URL_TEMPLATED, ut, traceId) if 'unittest' in sys.modules.keys(): response.headers["UT"] = ut + if response: + pinpoint.add_trace_header_v2(Defines.PP_HTTP_STATUS_CODE, str( + response.status_code), traceId) + if response.status_code >= 400: + pinpoint.mark_as_error( + f'status_code:{response.status_code}', 'FastAPIRequestPlugin', 0, traceId) + return super().onEnd(traceId, response) diff --git a/plugins/PY/pinpointPy/Fastapi/PinTranscation.py b/plugins/PY/pinpointPy/Fastapi/PinTranscation.py index 8c168c6d6..b4d3e7fca 100644 --- a/plugins/PY/pinpointPy/Fastapi/PinTranscation.py +++ b/plugins/PY/pinpointPy/Fastapi/PinTranscation.py @@ -17,7 +17,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pinpointPy.Fastapi.AsyCommon import AsynPinTrace +from pinpointPy.Fastapi.AsyCommon import AsyncPinTrace from pinpointPy import Defines,pinpoint from pinpointPy.Common import GenPinHeader from starlette_context import request_cycle_context @@ -36,7 +36,7 @@ async def pinpointTrace(*args, **kwargs): return pinpointTrace -class PinTransaction(AsynPinTrace): +class PinTransaction(AsyncPinTrace): def __init__(self, name: str, userGenHeaderCb: GenPinHeader): """pinpointPy user entry point diff --git a/plugins/PY/pinpointPy/Fastapi/_MotorMongo/__init__.py b/plugins/PY/pinpointPy/Fastapi/_MotorMongo/__init__.py index 998bf2ffa..911ff9e6f 100644 --- a/plugins/PY/pinpointPy/Fastapi/_MotorMongo/__init__.py +++ b/plugins/PY/pinpointPy/Fastapi/_MotorMongo/__init__.py @@ -20,17 +20,18 @@ # create by eelu from pinpointPy.Interceptor import intercept_once +from pinpointPy import logger @intercept_once def monkey_patch(): try: from pymongo import monitoring - from .motorComandPlugins import CommandLogger + from .motorCommandPlugins import CommandLogger monitoring.register(CommandLogger()) except ImportError as e: # do nothing - print(e) + logger.debug(f"not found pymongo. {e}") __all__ = ['monkey_patch'] diff --git a/plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorComandPlugins.py b/plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorCommandPlugins.py similarity index 63% rename from plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorComandPlugins.py rename to plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorCommandPlugins.py index b04060d0a..75626b5af 100644 --- a/plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorComandPlugins.py +++ b/plugins/PY/pinpointPy/Fastapi/_MotorMongo/motorCommandPlugins.py @@ -21,59 +21,63 @@ from pymongo import monitoring -from pinpointPy.Fastapi import AsyCommon -from pinpointPy import pinpoint,Defines +from pinpointPy.Fastapi import AsyCommon +from pinpointPy import pinpoint, Defines -class MotorComandPlugins(AsyCommon.AsynPinTrace): + +class MotorCommandPlugins(AsyCommon.AsyncPinTrace): def __init__(self, name): super().__init__(name) - def onBefore(self,parentId,*args, **kwargs): - traceId,_,_=super().onBefore(parentId,*args,**kwargs) + def onBefore(self, parentId, *args, **kwargs): + traceId, _, _ = super().onBefore(parentId, *args, **kwargs) event = args[0] ############################################################### - pinpoint.add_trace_header(Defines.PP_INTERCEPTOR_NAME, event.command_name, traceId) - pinpoint.add_trace_header(Defines.PP_SERVER_TYPE, Defines.PP_MONGDB_EXE_QUERY,traceId) - pinpoint.add_trace_header(Defines.PP_DESTINATION, event.connection_id[0], traceId) + pinpoint.add_trace_header( + Defines.PP_INTERCEPTOR_NAME, event.command_name, traceId) + pinpoint.add_trace_header( + Defines.PP_SERVER_TYPE, Defines.PP_MONGDB_EXE_QUERY, traceId) + pinpoint.add_trace_header( + Defines.PP_DESTINATION, event.connection_id[0], traceId) ############################################################### return event - def onEnd(self,traceId, ret): - super().onEnd(traceId,ret) + def onEnd(self, traceId, ret): + super().onEnd(traceId, ret) return ret - def onException(self,traceId, e): - pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e),traceId) + def onException(self, traceId, e): + pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e), traceId) class CommandLogger(monitoring.CommandListener): def __init__(self) -> None: - self.CommandPlugins={} + self.CommandPlugins = {} def started(self, event): - sampled,parentId = MotorComandPlugins.isSample() + sampled, parentId = MotorCommandPlugins.isSample() if not sampled: - return + return if event.command_name not in self.CommandPlugins: - self.CommandPlugins[event.command_name] = MotorComandPlugins(event.command_name) + self.CommandPlugins[event.command_name] = MotorCommandPlugins( + event.command_name) plugin = self.CommandPlugins[event.command_name] - plugin.onBefore(parentId,event) + plugin.onBefore(parentId, event) def succeeded(self, event): - sampled,traceId= MotorComandPlugins.isSample() + sampled, traceId = MotorCommandPlugins.isSample() if not sampled: - return + return plugin = self.CommandPlugins[event.command_name] - plugin.onEnd(traceId,None) - + plugin.onEnd(traceId, None) def failed(self, event): - sampled,traceId= MotorComandPlugins.isSample() + sampled, traceId = MotorCommandPlugins.isSample() if not sampled: - return + return plugin = self.CommandPlugins[event.command_name] - plugin.onException(traceId,None) - plugin.onEnd(traceId,None) + plugin.onException(traceId, None) + plugin.onEnd(traceId, None) diff --git a/plugins/PY/pinpointPy/Fastapi/__init__.py b/plugins/PY/pinpointPy/Fastapi/__init__.py index 049b38ff9..57b332dae 100644 --- a/plugins/PY/pinpointPy/Fastapi/__init__.py +++ b/plugins/PY/pinpointPy/Fastapi/__init__.py @@ -22,6 +22,7 @@ from pinpointPy.Fastapi.AsyCommonPlugin import CommonPlugin from pinpointPy.Fastapi.middleware import PinPointMiddleWare, FastAPIRequestPlugin from pinpointPy.Common import PinHeader, GenPinHeader +from pinpointPy.pinpoint import logger def __monkey_patch(*args, **kwargs): @@ -31,14 +32,17 @@ def __monkey_patch(*args, **kwargs): monkey_patch = getattr(module, 'monkey_patch') if callable(monkey_patch): monkey_patch() - print("try to install pinpointPy.Fastapi.%s module" % (key)) + logger.debug( + "try to install pinpointPy.Fastapi.%s module" % (key)) -def asyn_monkey_patch_for_pinpoint(AioRedis=True, MotorMongo=True, httpx=True): - __monkey_patch(aioredis=AioRedis, MotorMongo=MotorMongo, httpx=httpx) +def async_monkey_patch_for_pinpoint(AioRedis=True, MotorMongo=True, httpx=True, + sqlalchemy=True): + __monkey_patch(_aioredis=AioRedis, _MotorMongo=MotorMongo, _httpx=httpx, + _sqlalchemy=sqlalchemy) __version__ = '0.0.2' __author__ = 'liu.mingyi@navercorp.com' -__all__ = ['asyn_monkey_patch_for_pinpoint', 'FastAPIRequestPlugin', 'PinPointMiddleWare', +__all__ = ['async_monkey_patch_for_pinpoint', 'FastAPIRequestPlugin', 'PinPointMiddleWare', 'CommonPlugin', 'PinTransaction', 'PinHeader', 'GenPinHeader', 'PinStarlettePlugin'] diff --git a/plugins/PY/pinpointPy/Fastapi/_aioredis/AioRedisPlugins.py b/plugins/PY/pinpointPy/Fastapi/_aioredis/AioRedisPlugins.py index 3e0d9d1a0..94d0dd1c3 100644 --- a/plugins/PY/pinpointPy/Fastapi/_aioredis/AioRedisPlugins.py +++ b/plugins/PY/pinpointPy/Fastapi/_aioredis/AioRedisPlugins.py @@ -23,28 +23,30 @@ from pinpointPy import pinpoint, Defines -class AioRedisPlugins(AsyCommon.AsynPinTrace): +class AioRedisPlugins(AsyCommon.AsyncPinTrace): def __init__(self, name): super().__init__(name) - def onBefore(self,parentId, *args, **kwargs): - traceId,args,kwargs= super().onBefore(parentId,*args, **kwargs) + def onBefore(self, parentId, *args, **kwargs): + traceId, args, kwargs = super().onBefore(parentId, *args, **kwargs) ############################################################### - pinpoint.add_trace_header(Defines.PP_INTERCEPTOR_NAME, self.getFuncUniqueName(), traceId) - pinpoint.add_trace_header(Defines.PP_SERVER_TYPE, Defines.PP_REDIS, traceId) + pinpoint.add_trace_header( + Defines.PP_INTERCEPTOR_NAME, self.getFuncUniqueName(), traceId) + pinpoint.add_trace_header( + Defines.PP_SERVER_TYPE, Defines.PP_REDIS, traceId) dst = (str(args[0]).split("<")[3]).strip(">") pinpoint.add_trace_header(Defines.PP_DESTINATION, dst, traceId) pinpoint.add_trace_header_v2(Defines.PP_RETURN, str(args[1:]), traceId) ############################################################### - return traceId,args, kwargs + return traceId, args, kwargs - def onEnd(self,traceId, ret): + def onEnd(self, traceId, ret): ############################################################### # add_trace_header_v2(PP_RETURN,str(ret)) ############################################################### - super().onEnd(traceId,ret) + super().onEnd(traceId, ret) return ret - def onException(self,traceId, e): - pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e),traceId) + def onException(self, traceId, e): + pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e), traceId) diff --git a/plugins/PY/pinpointPy/Fastapi/_aioredis/__init__.py b/plugins/PY/pinpointPy/Fastapi/_aioredis/__init__.py index ef480a0da..f1c026db5 100644 --- a/plugins/PY/pinpointPy/Fastapi/_aioredis/__init__.py +++ b/plugins/PY/pinpointPy/Fastapi/_aioredis/__init__.py @@ -19,7 +19,9 @@ # Created by suwei at 8/20/20 -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -34,7 +36,7 @@ def monkey_patch(): interceptor.enable() except ImportError as e: - # do nothing - print(e) + logger.debug(f"not found aioredis. {e}") + -__all__=['monkey_patch'] \ No newline at end of file +__all__ = ['monkey_patch'] diff --git a/plugins/PY/pinpointPy/Fastapi/_httpx/__init__.py b/plugins/PY/pinpointPy/Fastapi/_httpx/__init__.py index 7afc3a147..2865337d4 100644 --- a/plugins/PY/pinpointPy/Fastapi/_httpx/__init__.py +++ b/plugins/PY/pinpointPy/Fastapi/_httpx/__init__.py @@ -26,7 +26,7 @@ @intercept_once def monkey_patch(): try: - from _httpx import AsyncClient + from httpx import AsyncClient from .httpxPlugins import HttpxRequestPlugins Interceptors = [ Interceptor(AsyncClient, 'request', HttpxRequestPlugins) @@ -34,9 +34,10 @@ def monkey_patch(): for interceptor in Interceptors: interceptor.enable() - except ImportError as e: logger.debug(f"import httpx:{e}") + except: + logger.info(f"unknown error in httpx module") __all__ = ['monkey_patch'] diff --git a/plugins/PY/pinpointPy/Fastapi/_httpx/httpxPlugins.py b/plugins/PY/pinpointPy/Fastapi/_httpx/httpxPlugins.py index cdb7b1953..9daef8e7a 100644 --- a/plugins/PY/pinpointPy/Fastapi/_httpx/httpxPlugins.py +++ b/plugins/PY/pinpointPy/Fastapi/_httpx/httpxPlugins.py @@ -18,16 +18,12 @@ # limitations under the License. # ****************************************************************************** - -from os import stat -from pickle import FALSE -from random import sample from pinpointPy.Fastapi import AsyCommon from pinpointPy import pinpoint, Defines, Helper from urllib.parse import urlparse -class HttpxRequestPlugins(AsyCommon.AsynPinTrace): +class HttpxRequestPlugins(AsyCommon.AsyncPinTrace): def __init__(self, name): super().__init__(name) @@ -35,24 +31,24 @@ def __init__(self, name): @staticmethod def isSample(*args, **kwargs): ''' - if not root, no trace + if not root,does no trace it :return: ''' - sampled, parentId = AsyCommon.AsynPinTrace.isSample(*args, **kwargs) + sampled, parentId, _, _ = AsyCommon.AsyncPinTrace.isSample( + *args, **kwargs) if not sampled: return False, None - - url = args[0][2] + url = args[2] target = urlparse(url).netloc if "headers" not in kwargs or not kwargs['headers']: kwargs["headers"] = {} if pinpoint.get_context(Defines.PP_HEADER_PINPOINT_SAMPLED, parentId) == "s1": Helper.generatePinpointHeader(target, kwargs['headers'], parentId) - return True, parentId + return True, parentId, args, kwargs else: kwargs['headers'][Defines.PP_HEADER_PINPOINT_SAMPLED] = Defines.PP_NOT_SAMPLED - return False, None + return False, None, args, kwargs def onBefore(self, parentId, *args, **kwargs): url = args[2] @@ -73,25 +69,13 @@ def onEnd(self, traceId, ret): ############################################################### pinpoint.add_trace_header(Defines.PP_NEXT_SPAN_ID, pinpoint.get_context( Defines.PP_NEXT_SPAN_ID, traceId), traceId) - pinpoint.add_trace_header_v2( - Defines.PP_HTTP_STATUS_CODE, str(ret.status_code), traceId) - pinpoint.add_trace_header_v2(Defines.PP_RETURN, str(ret), traceId) + if ret: + pinpoint.add_trace_header_v2( + Defines.PP_HTTP_STATUS_CODE, str(ret.status_code), traceId) + pinpoint.add_trace_header_v2(Defines.PP_RETURN, str(ret), traceId) ############################################################### super().onEnd(traceId, ret) return ret def onException(self, traceId, e): pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e), traceId) - - def get_arg(self, *args, **kwargs): - args_tmp = {} - j = 0 - - for i in args: - args_tmp["arg[" + str(j) + "]"] = (str(i)) - j += 1 - - for k in kwargs: - args_tmp[k] = kwargs[k] - - return str(args_tmp) diff --git a/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/__init__.py b/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/__init__.py new file mode 100644 index 000000000..e1bbcd4db --- /dev/null +++ b/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/__init__.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +# ------------------------------------------------------------------------------ +# Copyright 2020. NAVER Corp. - +# - +# Licensed under the Apache License, Version 2.0 (the "License"); - +# you may not use this file except in compliance with the License. - +# You may obtain a copy of the License at - +# - +# http://www.apache.org/licenses/LICENSE-2.0 - +# - +# Unless required by applicable law or agreed to in writing, software - +# distributed under the License is distributed on an "AS IS" BASIS, - +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - +# See the License for the specific language governing permissions and - +# limitations under the License. - +# ------------------------------------------------------------------------------ + +# Created by eeliu at 11/8/23 + +from pinpointPy.Interceptor import intercept_once, Interceptor +from pinpointPy import logger + + +@intercept_once +def monkey_patch(): + try: + import sqlalchemy + from .sqlalchemyPlugin import CreateEnginePlugin + Interceptors = [ + Interceptor(sqlalchemy, 'create_engine', CreateEnginePlugin) + ] + + for interceptor in Interceptors: + interceptor.enable() + except ImportError: + logger.debug("") + + +__all__ = ['monkey_patch'] + +__version__ = '0.0.1' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/sqlalchemyPlugin.py b/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/sqlalchemyPlugin.py new file mode 100644 index 000000000..c3faf8f2b --- /dev/null +++ b/plugins/PY/pinpointPy/Fastapi/_sqlalchemy/sqlalchemyPlugin.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +# ------------------------------------------------------------------------------ +# Copyright 2020. NAVER Corp. - +# - +# Licensed under the Apache License, Version 2.0 (the "License"); - +# you may not use this file except in compliance with the License. - +# You may obtain a copy of the License at - +# - +# http://www.apache.org/licenses/LICENSE-2.0 - +# - +# Unless required by applicable law or agreed to in writing, software - +# distributed under the License is distributed on an "AS IS" BASIS, - +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - +# See the License for the specific language governing permissions and - +# limitations under the License. - +# ------------------------------------------------------------------------------ + +# Created by eeliu at 7/31/20 + + +from pinpointPy import pinpoint, Defines +from sqlalchemy import event +from sqlalchemy.engine import Engine +from urllib.parse import urlparse + +from pinpointPy.Common import Trace +from pinpointPy.Fastapi.AsyCommon import AsyncPinTrace, TraceContext + + +class CreateEnginePlugin(Trace): + def onBefore(self, *args, **kwargs): + pass + + def onEnd(self, ret): + if isinstance(ret, Engine): + event.listen(ret, "before_cursor_execute", before_cursor_execute) + event.listen(ret, "after_cursor_execute", after_cursor_execute) + return ret + + +@event.listens_for(Engine, "before_cursor_execute") +def before_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + hasParent, parentId, _, _ = AsyncPinTrace.isSample() + if hasParent: + trace_id = pinpoint.with_trace(parentId) + TraceContext.set_parent_id(trace_id) + pinpoint.add_trace_header( + Defines.PP_INTERCEPTOR_NAME, 'before_cursor_execute', trace_id=trace_id) + pinpoint.add_trace_header( + Defines.PP_SQL_FORMAT, statement, trace_id=trace_id) + # pinpoint.add_trace_header_v2( + # Defines.PP_ARGS, 'user not cared', trace_id=trace_id) + DBUrl = urlparse(str(conn.engine.url)) + if 'mysql' in DBUrl.scheme: + pinpoint.add_trace_header( + Defines.PP_SERVER_TYPE, Defines.PP_MYSQL, trace_id=trace_id) + + if 'postgresql' in DBUrl.scheme: + pinpoint.add_trace_header( + Defines.PP_SERVER_TYPE, Defines.PP_POSTGRESQL, trace_id=trace_id) + + pinpoint.add_trace_header( + Defines.PP_DESTINATION, DBUrl.hostname, trace_id=trace_id) + context.__pinpoint__ = trace_id + + +@event.listens_for(Engine, "after_cursor_execute") +def after_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + if hasattr(context, '__pinpoint__'): + trace_id = pinpoint.end_trace(context.__pinpoint__) + TraceContext.set_parent_id(trace_id) diff --git a/plugins/PY/pinpointPy/WSGIPlugin.py b/plugins/PY/pinpointPy/WSGIPlugin.py index 9e30f8efd..2cb1d3032 100644 --- a/plugins/PY/pinpointPy/WSGIPlugin.py +++ b/plugins/PY/pinpointPy/WSGIPlugin.py @@ -20,8 +20,7 @@ # Created by eeliu at 11/9/20 from pinpointPy.Common import PinTrace -from pinpointPy import Helper -from pinpointPy import pinpoint +from pinpointPy import Helper, pinpoint class WSGIPlugin(PinTrace): diff --git a/plugins/PY/pinpointPy/__init__.py b/plugins/PY/pinpointPy/__init__.py index 00d312e72..bb4c7fb11 100644 --- a/plugins/PY/pinpointPy/__init__.py +++ b/plugins/PY/pinpointPy/__init__.py @@ -20,7 +20,6 @@ from pinpointPy.libs import monkey_patch_for_pinpoint from pinpointPy.pinpoint import set_agent, app_id, app_name, gen_tid, logger -# from .PinTranscation import PinHeader,GenPinHeader,PinTranscation __all__ = ['monkey_patch_for_pinpoint', 'set_agent', 'app_id', 'app_name', 'gen_tid', 'logger'] diff --git a/plugins/PY/pinpointPy/libs/DjangoRest/__init__.py b/plugins/PY/pinpointPy/libs/DjangoRest/__init__.py index 5a8df3b38..149aaf413 100644 --- a/plugins/PY/pinpointPy/libs/DjangoRest/__init__.py +++ b/plugins/PY/pinpointPy/libs/DjangoRest/__init__.py @@ -15,20 +15,24 @@ # ------------------------------------------------------------------------------ from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger @intercept_once def monkey_patch(): - from rest_framework.views import APIView - from .ViewsPlugin import ViewsPlugin + try: + from rest_framework.views import APIView + from .ViewsPlugin import ViewsPlugin - Interceptors = [ - Interceptor(APIView, 'dispatch', ViewsPlugin), - ] - for interceptor in Interceptors: - interceptor.enable() + Interceptors = [ + Interceptor(APIView, 'dispatch', ViewsPlugin), + ] + for interceptor in Interceptors: + interceptor.enable() + except ImportError as e: + logger.debug(f'import DjangoRest {e}') __all__ = ['monkey_patch'] -__version__ = '0.0.1' +__version__ = '0.0.2' __author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/MySQLdb/__init__.py b/plugins/PY/pinpointPy/libs/MySQLdb/__init__.py index 710214c39..79414e006 100644 --- a/plugins/PY/pinpointPy/libs/MySQLdb/__init__.py +++ b/plugins/PY/pinpointPy/libs/MySQLdb/__init__.py @@ -17,6 +17,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger @intercept_once @@ -32,9 +33,9 @@ def monkey_patch(): interceptor.enable() except ImportError as e: - print(e) + logger.info(f'exception at {e}') __all__ = ['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/MysqlConnector/__init__.py b/plugins/PY/pinpointPy/libs/MysqlConnector/__init__.py index ab9b2a0b9..a102da14b 100644 --- a/plugins/PY/pinpointPy/libs/MysqlConnector/__init__.py +++ b/plugins/PY/pinpointPy/libs/MysqlConnector/__init__.py @@ -14,7 +14,9 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -24,7 +26,6 @@ def monkey_patch(): from .MysqlPlugin import MysqlPlugin from .CMysqlPlugin import CMysqlPlugin - Interceptors = [ Interceptor(MySQLCursor, 'execute', MysqlPlugin), Interceptor(MySQLCursorPrepared, 'execute', MysqlPlugin), @@ -34,11 +35,11 @@ def monkey_patch(): for interceptor in Interceptors: interceptor.enable() - except ImportError as e: # do nothing - print(e) + logger.debug(f'import mysql.connector.* {e}') + -__all__=['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__all__ = ['monkey_patch'] +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/PyMysql/__init__.py b/plugins/PY/pinpointPy/libs/PyMysql/__init__.py index b41e0c043..fdb5b6cb3 100644 --- a/plugins/PY/pinpointPy/libs/PyMysql/__init__.py +++ b/plugins/PY/pinpointPy/libs/PyMysql/__init__.py @@ -14,7 +14,9 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -23,19 +25,17 @@ def monkey_patch(): from pymysql.cursors import Cursor from .PyMysqlPlugin import PyMysqlPlugin - Interceptors = [ Interceptor(Cursor, 'execute', PyMysqlPlugin), ] for interceptor in Interceptors: interceptor.enable() - except ImportError as e: - # do nothing - print(e) + logger.info(f'exception at {e}') + -__all__=['monkey_patch'] +__all__ = ['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/__init__.py b/plugins/PY/pinpointPy/libs/__init__.py index 5da21a092..32f54115c 100644 --- a/plugins/PY/pinpointPy/libs/__init__.py +++ b/plugins/PY/pinpointPy/libs/__init__.py @@ -26,7 +26,7 @@ def __monkey_patch(*args, **kwargs): try: monkey_patch() except Exception as e: - logger.info("exception at {}", e) + logger.info(f'exception at {e}') def monkey_patch_for_pinpoint(pymongo=True, @@ -43,5 +43,5 @@ def monkey_patch_for_pinpoint(pymongo=True, __all__ = ['monkey_patch_for_pinpoint'] -__version__ = '0.0.1' +__version__ = '0.0.2' __author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/pyRedis/__init__.py b/plugins/PY/pinpointPy/libs/pyRedis/__init__.py index 618cb54b5..830291007 100644 --- a/plugins/PY/pinpointPy/libs/pyRedis/__init__.py +++ b/plugins/PY/pinpointPy/libs/pyRedis/__init__.py @@ -19,7 +19,9 @@ # Created by eeliu at 8/20/20 -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -34,9 +36,9 @@ def monkey_patch(): interceptor.enable() except ImportError as e: - # do nothing - print(e) + logger.info(f'exception at {e}') + -__all__=['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__all__ = ['monkey_patch'] +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/pymongo/__init__.py b/plugins/PY/pinpointPy/libs/pymongo/__init__.py index bf290cc63..969e1b38a 100644 --- a/plugins/PY/pinpointPy/libs/pymongo/__init__.py +++ b/plugins/PY/pinpointPy/libs/pymongo/__init__.py @@ -18,7 +18,9 @@ # ------------------------------------------------------------------------------ # Created by eeliu at 8/20/20 -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -28,7 +30,7 @@ def monkey_patch(): from .MongoClientPlugin import MongoClientPlugin # print(pymongo.collection) Interceptors = [ - Interceptor(Collection,'find', MongoClientPlugin), + Interceptor(Collection, 'find', MongoClientPlugin), # Interceptor(Collection, 'insert', MongoClientPlugin), # Interceptor(Collection, 'update', MongoClientPlugin), Interceptor(Collection, 'update_many', MongoClientPlugin), @@ -37,14 +39,15 @@ def monkey_patch(): Interceptor(Collection, 'insert_many', MongoClientPlugin), Interceptor(Collection, 'insert_one', MongoClientPlugin), ] - + for interceptor in Interceptors: interceptor.enable() - except ImportError: - pass + except ImportError as e: + logger.info(f'exception at {e}') + -__all__=['monkey_patch'] +__all__ = ['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/requests/__init__.py b/plugins/PY/pinpointPy/libs/requests/__init__.py index 917121fa0..40d1f6cc0 100644 --- a/plugins/PY/pinpointPy/libs/requests/__init__.py +++ b/plugins/PY/pinpointPy/libs/requests/__init__.py @@ -15,7 +15,9 @@ # ------------------------------------------------------------------------------ -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -26,7 +28,7 @@ def monkey_patch(): from .NextSpanPlugin import NextSpanPlugin Interceptors = [ - Interceptor(requests,'post', NextSpanPlugin), + Interceptor(requests, 'post', NextSpanPlugin), Interceptor(requests, 'get', NextSpanPlugin), Interceptor(requests, 'patch', NextSpanPlugin), ] @@ -35,9 +37,10 @@ def monkey_patch(): interceptor.enable() except ImportError: - pass + logger.info(f'exception at {e}') + -__all__=['monkey_patch'] +__all__ = ['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/libs/urllib/__init__.py b/plugins/PY/pinpointPy/libs/urllib/__init__.py index ead7236be..284681b1f 100644 --- a/plugins/PY/pinpointPy/libs/urllib/__init__.py +++ b/plugins/PY/pinpointPy/libs/urllib/__init__.py @@ -19,7 +19,9 @@ # Created by eeliu at 8/20/20 -from pinpointPy.Interceptor import Interceptor,intercept_once +from pinpointPy.Interceptor import Interceptor, intercept_once +from pinpointPy import logger + @intercept_once def monkey_patch(): @@ -28,16 +30,17 @@ def monkey_patch(): from .UrlOpenPlugin import UrlOpenPlugin Interceptors = [ - Interceptor(urllib.request,'urlopen', UrlOpenPlugin) + Interceptor(urllib.request, 'urlopen', UrlOpenPlugin) ] for interceptor in Interceptors: interceptor.enable() - except ImportError: - pass + except ImportError as e: + logger.info(f'exception at {e}') + -__all__=['monkey_patch'] +__all__ = ['monkey_patch'] -__version__ = '0.0.1' -__author__ = 'liu.mingyi@navercorp.com' \ No newline at end of file +__version__ = '0.0.2' +__author__ = 'liu.mingyi@navercorp.com' diff --git a/plugins/PY/pinpointPy/pinpoint.py b/plugins/PY/pinpointPy/pinpoint.py index 86be8d486..a59861828 100644 --- a/plugins/PY/pinpointPy/pinpoint.py +++ b/plugins/PY/pinpointPy/pinpoint.py @@ -20,11 +20,24 @@ import random import logging import _pinpointPy +import sys __app_id = 'app_id_str' __app_name = 'app_name_str' -logger = logging.Logger('pinpointPy') + +def __default_logger(): + logger = logging.Logger('pinpoint') + ch = logging.StreamHandler(sys.stdout) + ch.setLevel(logging.INFO) + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + logger.addHandler(ch) + return logger + + +logger = __default_logger() def app_id(): @@ -74,8 +87,8 @@ def trace_has_root(trace_id=-1): return _pinpointPy.trace_has_root(trace_id) -def mark_as_error(message: str, filename: str = '', line: int = 0): - _pinpointPy.mark_as_error(message, filename, line) +def mark_as_error(message: str, filename: str = '', line: int = 0, trace_id: int = -1): + _pinpointPy.mark_as_error(message, filename, line, trace_id) def drop_trace(trace_id: int = -1): @@ -86,13 +99,16 @@ def check_trace_limit(time: int = -1) -> bool: return _pinpointPy.check_tracelimit(time) -def set_agent(app_id_str: str, app_name_str: str, collect_agent_host: str, trace_limit: int = -1, debug_callback=True): +def set_agent(app_id_str: str, app_name_str: str, collect_agent_host: str, trace_limit: int = -1, log_level=logging.DEBUG): global __app_id, __app_name __app_id = app_id_str __app_name = app_name_str _pinpointPy.set_agent(collect_agent_host, trace_limit) - if type(debug_callback) == bool and debug_callback: - _pinpointPy.enable_debug(debug_callback) - elif callable(debug_callback): - # todo : check type is [[str]->] - _pinpointPy.enable_debug(debug_callback) + global logger + logger.setLevel(log_level) + if log_level == logging.DEBUG: + def debug_func(msg: str): + logger.debug(msg) + _pinpointPy.enable_debug(debug_func) + logger.debug( + f"appid:{app_id_str} appname:{app_name_str} collector_agent:{collect_agent_host} trace_limit:{trace_limit} log_level:{log_level}") diff --git a/testapps/PY/requirements.txt b/testapps/PY/requirements.txt index dbe6c7a53..c8a1de8c9 100644 --- a/testapps/PY/requirements.txt +++ b/testapps/PY/requirements.txt @@ -1,5 +1,4 @@ Flask==3.0.0 -pinpointPy==1.0.15 PyMySQL==1.1.0 redis==5.0.1 Requests==2.31.0 diff --git a/testapps/backend.dockerfile b/testapps/backend.dockerfile new file mode 100644 index 000000000..47fba2405 --- /dev/null +++ b/testapps/backend.dockerfile @@ -0,0 +1,15 @@ +FROM python:3.8 +WORKDIR /app/ + +COPY testapps/backend/ /app/ +RUN pip install -r requirements.txt + +COPY setup.py /pinpoint-c-agent/setup.py +COPY common/ /pinpoint-c-agent/common +COPY README /pinpoint-c-agent/README +COPY plugins/PY /pinpoint-c-agent/plugins/PY +COPY src/PY /pinpoint-c-agent/src/PY +RUN cd /pinpoint-c-agent && pip install -e . + +# EXPOSE 8000 +CMD [ "uvicorn", "main:app","--host=0.0.0.0","--port=8000","--reload" ] \ No newline at end of file diff --git a/testapps/backend/__init__.py b/testapps/backend/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/testapps/backend/crud.py b/testapps/backend/crud.py new file mode 100644 index 000000000..ade384c09 --- /dev/null +++ b/testapps/backend/crud.py @@ -0,0 +1,36 @@ +from sqlalchemy.orm import Session + +import models, schemas + + +def get_user(db: Session, user_id: int): + return db.query(models.User).filter(models.User.id == user_id).first() + + +def get_user_by_email(db: Session, email: str): + return db.query(models.User).filter(models.User.email == email).first() + + +def get_users(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.User).offset(skip).limit(limit).all() + + +def create_user(db: Session, user: schemas.UserCreate): + fake_hashed_password = user.password + "notreallyhashed" + db_user = models.User(email=user.email, hashed_password=fake_hashed_password) + db.add(db_user) + db.commit() + db.refresh(db_user) + return db_user + + +def get_items(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Item).offset(skip).limit(limit).all() + + +def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): + db_item = models.Item(**item.dict(), owner_id=user_id) + db.add(db_item) + db.commit() + db.refresh(db_item) + return db_item diff --git a/testapps/backend/database.py b/testapps/backend/database.py new file mode 100644 index 000000000..b69b3bd44 --- /dev/null +++ b/testapps/backend/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +# SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" +SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:password@dev-mysql/employees?charset=utf8mb4" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() diff --git a/testapps/backend/main.py b/testapps/backend/main.py new file mode 100644 index 000000000..f439be9a5 --- /dev/null +++ b/testapps/backend/main.py @@ -0,0 +1,135 @@ +from fastapi import FastAPI, Request, Response, Depends, HTTPException +from starlette.middleware import Middleware +from pinpointPy.Fastapi import PinPointMiddleWare, async_monkey_patch_for_pinpoint +from pinpointPy import set_agent +from sqlalchemy.orm import Session +from starlette_context.middleware import ContextMiddleware +import aioredis +from contextlib import asynccontextmanager +import httpx +from typing import List + +import crud +import models +import schemas + +from database import SessionLocal, engine + +models.Base.metadata.create_all(bind=engine) + +redis = aioredis.from_url('redis://redis:6379', decode_responses=True) + + +class UserMiddleWare(PinPointMiddleWare): + async def dispatch(self, request: Request, call_next): + if request.url.path in ["/heartbeat", "/l7check"]: + return await call_next(request) + else: + return await super().dispatch(request, call_next) + + +middleware = [ + Middleware(ContextMiddleware), + Middleware(UserMiddleWare) +] + +async_monkey_patch_for_pinpoint() + +set_agent("cd.dev.test.py.backend", "cd.dev.test.py.backend", 'tcp:dev-collector:10000') + + +@asynccontextmanager +async def lifespan(app: FastAPI): + app.requests_client = httpx.AsyncClient() + yield + await app.requests_client.aclose() + +app = FastAPI(title='FastAPI Pinpoint Example', + middleware=middleware, lifespan=lifespan) + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +@app.middleware("http") +async def db_session_middleware(request: Request, call_next): + response = Response("Internal server error", status_code=500) + try: + request.state.db = SessionLocal() + response = await call_next(request) + finally: + request.state.db.close() + return response + + +def get_db(request: Request): + return request.state.db + + +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +@app.get("/test-redis/set/{uid}", tags=["redis"]) +async def test_redis(uid: str = "default"): + await redis.set(uid, "50fdf310-7d3b-11ee-b962-0242ac120002", ex=1) + in_value = await redis.get(uid) + return {"uid": in_value} + + +@app.get("/httpx/example", tags=["httpx"]) +async def test_httpx(request: Request, url='http://www.example.com/'): + requests_client = request.app.requests_client + print(request.headers) + response = await requests_client.get(url) + return {"response": response.status_code} + + +@app.get("/httpx-self/", tags=["httpx"]) +async def test_httpx(request: Request): + requests_client = request.app.requests_client + response = await requests_client.get('http://127.0.0.1:8000/httpx/example') + return {"response": response.status_code} + +# thanks guide from https://fastapi.tiangolo.com/tutorial/sql-databases/ + + +@app.post("/users/", response_model=schemas.User, tags=["mysql"]) +def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): + db_user = crud.get_user_by_email(db, email=user.email) + if db_user: + raise HTTPException(status_code=400, detail="Email already registered") + return crud.create_user(db=db, user=user) + + +@app.get("/users/", response_model=List[schemas.User], tags=["mysql"]) +def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + users = crud.get_users(db, skip=skip, limit=limit) + return users + + +@app.get("/users/{user_id}", response_model=schemas.User, tags=["mysql"]) +def read_user(user_id: int, db: Session = Depends(get_db)): + db_user = crud.get_user(db, user_id=user_id) + if db_user is None: + raise HTTPException(status_code=404, detail="User not found") + return db_user + + +@app.post("/users/{user_id}/items/", response_model=schemas.Item, tags=["mysql"]) +def create_item_for_user( + user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) +): + return crud.create_user_item(db=db, item=item, user_id=user_id) + + +@app.get("/items/", response_model=List[schemas.Item], tags=["mysql"]) +def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + items = crud.get_items(db, skip=skip, limit=limit) + return items diff --git a/testapps/backend/models.py b/testapps/backend/models.py new file mode 100644 index 000000000..fd291a025 --- /dev/null +++ b/testapps/backend/models.py @@ -0,0 +1,26 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from database import Base + + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + email = Column(String(20), unique=True, index=True) + hashed_password = Column(String(36)) + is_active = Column(Boolean, default=True) + + items = relationship("Item", back_populates="owner") + + +class Item(Base): + __tablename__ = "items" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String(128), index=True) + description = Column(String(256), index=True) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="items") diff --git a/testapps/backend/requirements.txt b/testapps/backend/requirements.txt new file mode 100644 index 000000000..55b04447f --- /dev/null +++ b/testapps/backend/requirements.txt @@ -0,0 +1,9 @@ +aioredis==2.0.1 +fastapi +httpx==0.25.1 +pydantic==2.4.2 +SQLAlchemy==2.0.23 +starlette +starlette_context +uvicorn +pymysql \ No newline at end of file diff --git a/testapps/backend/schemas.py b/testapps/backend/schemas.py new file mode 100644 index 000000000..c49beba88 --- /dev/null +++ b/testapps/backend/schemas.py @@ -0,0 +1,37 @@ +from typing import List, Union + +from pydantic import BaseModel + + +class ItemBase(BaseModel): + title: str + description: Union[str, None] = None + + +class ItemCreate(ItemBase): + pass + + +class Item(ItemBase): + id: int + owner_id: int + + class Config: + orm_mode = True + + +class UserBase(BaseModel): + email: str + + +class UserCreate(UserBase): + password: str + + +class User(UserBase): + id: int + is_active: bool + items: List[Item] = [] + + class Config: + orm_mode = True diff --git a/testapps/compose.yaml b/testapps/compose.yaml index c760e0ad8..eec419bef 100644 --- a/testapps/compose.yaml +++ b/testapps/compose.yaml @@ -17,14 +17,6 @@ services: volumes: - "./test_db:/app/" - testapp-php: - restart: always - build: - dockerfile: testapps/php.dockerfile - context: ../ - ports: - - 8185:80 - redis: image: redis:alpine command: redis-server --port 6379 @@ -37,9 +29,34 @@ services: testapp-flask: restart: always build: - dockerfile: flask.dockerfile + dockerfile: testapps/flask.dockerfile + context: ../ + ports: + - 8184:80 + + testapp-php: + restart: always + build: + dockerfile: testapps/php.dockerfile + context: ../ + ports: + - 8185:80 + + testapp-fastapi: + restart: always + build: + dockerfile: testapps/fastapi.dockerfile + context: ../ + ports: + - 8186:8000 + + backend: + restart: always + build: + dockerfile: testapps/backend.dockerfile + context: ../ ports: - - 8184:8184 + - 8187:8000 dev-collector: restart: always diff --git a/testapps/fastapi.dockerfile b/testapps/fastapi.dockerfile new file mode 100644 index 000000000..430bf3850 --- /dev/null +++ b/testapps/fastapi.dockerfile @@ -0,0 +1,15 @@ +FROM python:3.8 +WORKDIR /app/ + +COPY testapps/fastapi/ /app/ +RUN pip install -r requirements.txt + +COPY setup.py /pinpoint-c-agent/setup.py +COPY common/ /pinpoint-c-agent/common +COPY README /pinpoint-c-agent/README +COPY plugins/PY /pinpoint-c-agent/plugins/PY +COPY src/PY /pinpoint-c-agent/src/PY +RUN cd /pinpoint-c-agent && pip install -e . + +EXPOSE 8000 +CMD [ "uvicorn", "main:app","--host=0.0.0.0","--port=8000","--reload" ] \ No newline at end of file diff --git a/testapps/fastapi/__init__.py b/testapps/fastapi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/testapps/fastapi/crud.py b/testapps/fastapi/crud.py new file mode 100644 index 000000000..ade384c09 --- /dev/null +++ b/testapps/fastapi/crud.py @@ -0,0 +1,36 @@ +from sqlalchemy.orm import Session + +import models, schemas + + +def get_user(db: Session, user_id: int): + return db.query(models.User).filter(models.User.id == user_id).first() + + +def get_user_by_email(db: Session, email: str): + return db.query(models.User).filter(models.User.email == email).first() + + +def get_users(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.User).offset(skip).limit(limit).all() + + +def create_user(db: Session, user: schemas.UserCreate): + fake_hashed_password = user.password + "notreallyhashed" + db_user = models.User(email=user.email, hashed_password=fake_hashed_password) + db.add(db_user) + db.commit() + db.refresh(db_user) + return db_user + + +def get_items(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Item).offset(skip).limit(limit).all() + + +def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): + db_item = models.Item(**item.dict(), owner_id=user_id) + db.add(db_item) + db.commit() + db.refresh(db_item) + return db_item diff --git a/testapps/fastapi/database.py b/testapps/fastapi/database.py new file mode 100644 index 000000000..b69b3bd44 --- /dev/null +++ b/testapps/fastapi/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +# SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" +SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:password@dev-mysql/employees?charset=utf8mb4" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() diff --git a/testapps/fastapi/main.py b/testapps/fastapi/main.py new file mode 100644 index 000000000..7a4810383 --- /dev/null +++ b/testapps/fastapi/main.py @@ -0,0 +1,143 @@ +from fastapi import FastAPI, Request, Response, Depends, HTTPException +from starlette.middleware import Middleware +from pinpointPy.Fastapi import PinPointMiddleWare, async_monkey_patch_for_pinpoint +from pinpointPy import set_agent +from sqlalchemy.orm import Session +from starlette_context.middleware import ContextMiddleware +import aioredis +from contextlib import asynccontextmanager +import httpx +from typing import List + +import crud +import models +import schemas + +from database import SessionLocal, engine + +models.Base.metadata.create_all(bind=engine) + +redis = aioredis.from_url('redis://redis:6379', decode_responses=True) + + +class UserMiddleWare(PinPointMiddleWare): + async def dispatch(self, request: Request, call_next): + if request.url.path in ["/heartbeat", "/l7check"]: + return await call_next(request) + else: + return await super().dispatch(request, call_next) + + +middleware = [ + Middleware(ContextMiddleware), + Middleware(UserMiddleWare) +] + +async_monkey_patch_for_pinpoint() + +set_agent("cd.dev.test.py", "cd.dev.test.py", 'tcp:dev-collector:10000') + + +@asynccontextmanager +async def lifespan(app: FastAPI): + app.requests_client = httpx.AsyncClient() + yield + await app.requests_client.aclose() + +app = FastAPI(title='FastAPI Pinpoint Example', + middleware=middleware, lifespan=lifespan) + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +@app.middleware("http") +async def db_session_middleware(request: Request, call_next): + response = Response("Internal server error", status_code=500) + try: + request.state.db = SessionLocal() + response = await call_next(request) + finally: + request.state.db.close() + return response + + +def get_db(request: Request): + return request.state.db + + +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +@app.get("/test-redis/set/{uid}", tags=["redis"]) +async def test_redis(uid: str = "default"): + await redis.set(uid, "50fdf310-7d3b-11ee-b962-0242ac120002", ex=1) + in_value = await redis.get(uid) + return {"uid": in_value} + + +@app.get("/httpx/example", tags=["httpx"]) +async def test_httpx(request: Request, url='http://www.example.com/'): + requests_client = request.app.requests_client + print(request.headers) + response = await requests_client.get(url) + return {"response": response.status_code} + + +@app.get("/httpx/backend", tags=["httpx"]) +async def test_httpx_backend(request: Request, url='http://backend:8000/'): + requests_client = request.app.requests_client + print(request.headers) + response = await requests_client.get(url) + return {"response": response.status_code} + + +@app.get("/httpx-self/", tags=["httpx"]) +async def test_httpx(request: Request): + requests_client = request.app.requests_client + response = await requests_client.get('http://127.0.0.1:8000/httpx/example') + return {"response": response.status_code} + +# thanks guide from https://fastapi.tiangolo.com/tutorial/sql-databases/ + + +@app.post("/users/", response_model=schemas.User, tags=["mysql"]) +def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): + db_user = crud.get_user_by_email(db, email=user.email) + if db_user: + raise HTTPException(status_code=400, detail="Email already registered") + return crud.create_user(db=db, user=user) + + +@app.get("/users/", response_model=List[schemas.User], tags=["mysql"]) +def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + users = crud.get_users(db, skip=skip, limit=limit) + return users + + +@app.get("/users/{user_id}", response_model=schemas.User, tags=["mysql"]) +def read_user(user_id: int, db: Session = Depends(get_db)): + db_user = crud.get_user(db, user_id=user_id) + if db_user is None: + raise HTTPException(status_code=404, detail="User not found") + return db_user + + +@app.post("/users/{user_id}/items/", response_model=schemas.Item, tags=["mysql"]) +def create_item_for_user( + user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) +): + return crud.create_user_item(db=db, item=item, user_id=user_id) + + +@app.get("/items/", response_model=List[schemas.Item], tags=["mysql"]) +def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + items = crud.get_items(db, skip=skip, limit=limit) + return items diff --git a/testapps/fastapi/models.py b/testapps/fastapi/models.py new file mode 100644 index 000000000..fd291a025 --- /dev/null +++ b/testapps/fastapi/models.py @@ -0,0 +1,26 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from database import Base + + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + email = Column(String(20), unique=True, index=True) + hashed_password = Column(String(36)) + is_active = Column(Boolean, default=True) + + items = relationship("Item", back_populates="owner") + + +class Item(Base): + __tablename__ = "items" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String(128), index=True) + description = Column(String(256), index=True) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="items") diff --git a/testapps/fastapi/requirements.txt b/testapps/fastapi/requirements.txt new file mode 100644 index 000000000..55b04447f --- /dev/null +++ b/testapps/fastapi/requirements.txt @@ -0,0 +1,9 @@ +aioredis==2.0.1 +fastapi +httpx==0.25.1 +pydantic==2.4.2 +SQLAlchemy==2.0.23 +starlette +starlette_context +uvicorn +pymysql \ No newline at end of file diff --git a/testapps/fastapi/schemas.py b/testapps/fastapi/schemas.py new file mode 100644 index 000000000..c49beba88 --- /dev/null +++ b/testapps/fastapi/schemas.py @@ -0,0 +1,37 @@ +from typing import List, Union + +from pydantic import BaseModel + + +class ItemBase(BaseModel): + title: str + description: Union[str, None] = None + + +class ItemCreate(ItemBase): + pass + + +class Item(ItemBase): + id: int + owner_id: int + + class Config: + orm_mode = True + + +class UserBase(BaseModel): + email: str + + +class UserCreate(UserBase): + password: str + + +class User(UserBase): + id: int + is_active: bool + items: List[Item] = [] + + class Config: + orm_mode = True diff --git a/testapps/flask.dockerfile b/testapps/flask.dockerfile index fb872c6c1..aa939435c 100644 --- a/testapps/flask.dockerfile +++ b/testapps/flask.dockerfile @@ -1,8 +1,16 @@ FROM python:3.11 WORKDIR /app/ -COPY ./PY/ /app/ +COPY testapps/PY/ /app/ + +COPY setup.py /pinpoint-c-agent/setup.py +COPY common/ /pinpoint-c-agent/common +COPY README /pinpoint-c-agent/README +COPY plugins/PY /pinpoint-c-agent/plugins/PY +COPY src/PY /pinpoint-c-agent/src/PY + RUN pip install -r requirements.txt +RUN cd /pinpoint-c-agent && pip install -e . -EXPOSE 8184 +EXPOSE 80 CMD [ "python", "app.py" ] \ No newline at end of file diff --git a/testapps/php.dockerfile b/testapps/php.dockerfile index ef6f762d9..e3bcf44aa 100644 --- a/testapps/php.dockerfile +++ b/testapps/php.dockerfile @@ -13,4 +13,5 @@ COPY src/PHP /pinpoint-c-agent/src/PHP COPY common /pinpoint-c-agent/common COPY tests /pinpoint-c-agent/tests -RUN cd /pinpoint-c-agent/ && phpize && ./configure && make && make install \ No newline at end of file +RUN cd /pinpoint-c-agent/ && phpize && ./configure && make && make install +EXPOSE 80 \ No newline at end of file diff --git a/testapps/test_db b/testapps/test_db new file mode 160000 index 000000000..3c7fa05e0 --- /dev/null +++ b/testapps/test_db @@ -0,0 +1 @@ +Subproject commit 3c7fa05e04b4c339d91a43b7029a210212d48e6c