Skip to content

Commit

Permalink
feat(py): PostgreSql (#599)
Browse files Browse the repository at this point in the history
- psycopg2
- version :1.2.1
  • Loading branch information
eeliu authored Apr 17, 2024
1 parent b959d04 commit b851e34
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 14 deletions.
7 changes: 4 additions & 3 deletions plugins/PY/pinpointPy/libs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ def monkey_patch_for_pinpoint(pymongo=True,
urllib=True,
sqlalchemy=True,
MySQLdb=True,
MysqlConnector=True):
MysqlConnector=True,
pyscopg2=True):
__monkey_patch(_pymongo=pymongo, _MySQLdb=MySQLdb, _PyMysql=PyMysql, _pyRedis=pyRedis, _requests=requests,
_urllib=urllib, _sqlalchemy=sqlalchemy, _MysqlConnector=MysqlConnector)
_urllib=urllib, _sqlalchemy=sqlalchemy, _MysqlConnector=MysqlConnector, _psycopg2=pyscopg2)


__all__ = ['monkey_patch_for_pinpoint']
__version__ = '0.0.3'
__version__ = '0.0.4'
__author__ = 'liu.mingyi@navercorp.com'
100 changes: 100 additions & 0 deletions plugins/PY/pinpointPy/libs/_psycopg2/PsycopgPlugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# ------------------------------------------------------------------------------
# Copyright 2024. 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. -
# ------------------------------------------------------------------------------

from pinpointPy import Common, pinpoint, Defines
from functools import wraps
import psycopg2


class QueryPlugin(Common.PinTrace):

def __init__(self, name):
super().__init__(name)

def onBefore(self, parentId, *args, **kwargs):
trace_id, _, _ = super().onBefore(parentId, *args, **kwargs)
###############################################################
pinpoint.add_trace_header(
Defines.PP_INTERCEPTOR_NAME, self.getUniqueName(), trace_id)
pinpoint.add_trace_header(
Defines.PP_SERVER_TYPE, Defines.PP_POSTGRESQL, trace_id)
query = str(args[1])
pinpoint.add_trace_header(Defines.PP_SQL_FORMAT, query, trace_id)
###############################################################
cursor = args[0]
dst = cursor.connection.get_dsn_parameters()['host']
pinpoint.add_trace_header(
Defines.PP_DESTINATION, dst, trace_id=trace_id)
return trace_id, args, kwargs

def onEnd(self, trace_id, ret):
super().onEnd(trace_id, ret)
return ret

def onException(self, trace_id, e):
pinpoint.add_trace_header(Defines.PP_ADD_EXCEPTION, str(e), trace_id)


class FetchPlugin(QueryPlugin):
def onBefore(self, parentId, *args, **kwargs: None):
trace_id, _, _ = super(QueryPlugin, self).onBefore(
parentId, *args, **kwargs)
pinpoint.add_trace_header(
Defines.PP_INTERCEPTOR_NAME, self.getUniqueName(), trace_id)
return trace_id, args, kwargs


class LoggingCursor(psycopg2.extensions.cursor):
@QueryPlugin("execute")
def execute(self, sql, args=None):
return super().execute(sql, args)

@QueryPlugin("executemany")
def executemany(self, query, args=None):
return super().executemany(query, args)

@QueryPlugin("mogrify")
def mogrify(self, query, args=None):
return super().mogrify(query, args)

@FetchPlugin("fetchall")
def fetchall(self):
return super().fetchall()

@FetchPlugin("fetchmany")
def fetchmany(self, size: None):
return super().fetchmany(size)

@FetchPlugin("fetchone")
def fetchone(self):
return super().fetchone()


class ConnectionPlugin:
def __init__(self, name):
pass

def __call__(self, func):

@wraps(func)
def pinpointTrace(*args, **kwargs):
kwargs['cursor_factory'] = LoggingCursor
ret = func(*args, **kwargs)
return ret
return pinpointTrace
41 changes: 41 additions & 0 deletions plugins/PY/pinpointPy/libs/_psycopg2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# ------------------------------------------------------------------------------
# Copyright 2024. 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. -
# ------------------------------------------------------------------------------
from pinpointPy.Interceptor import Interceptor, intercept_once
from pinpointPy import get_logger
from .PsycopgPlugins import ConnectionPlugin


@intercept_once
def monkey_patch():

try:
import psycopg2
Interceptors = [
Interceptor(psycopg2, 'connect', ConnectionPlugin),
]
for interceptor in Interceptors:
interceptor.enable()
except ImportError as e:
get_logger().info(f'exception at {e}')


__all__ = ['monkey_patch']

__version__ = '0.0.1'
__author__ = 'liu.mingyi@navercorp.com'
66 changes: 66 additions & 0 deletions plugins/PY/pinpointPy/libs/_psycopg2/test_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import psycopg2.extensions
import logging
from pinpointPy.libs._psycopg2 import monkey_patch
import unittest
from pinpointPy import PinTransaction
from pinpointPy.tests import TestCase, GenTestHeader


class Test_Case(TestCase):

@classmethod
def setUpClass(cls):
super().setUpClass()
monkey_patch()

@PinTransaction("testcase", GenTestHeader())
def test_case(self):
# reference from https://www.psycopg.org/docs/usage.html
import psycopg2
conn = psycopg2.connect(
dbname="test", user="test", password="pinpoint", host="postgres", port=5432)
cur = conn.cursor()
cur.execute(
"CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)",
(100, "abc'def"))
cur.execute("SELECT * FROM test;")
resp = cur.fetchone()
conn.commit()
cur.close()
conn.close()

# import psycopg2

# class LoggingCursor(psycopg2.extensions.cursor):
# def execute(self, sql, args=None):
# logger = logging.getLogger('sql_debug')
# logger.info(self.mogrify(sql, args))

# try:
# print(f"start sql: {sql} {self.connection}")
# psycopg2.extensions.cursor.execute(self, sql, args)
# print(f"end sql: {sql}")
# except Exception as exc:
# logger.error("%s: %s" % (exc.__class__.__name__, exc))
# raise

# conn = psycopg2.connect(
# dbname="test", user="test", password="pinpoint", host="10.34.130.156", port=5432, cursor_factory=LoggingCursor)
# cur = conn.cursor()
# # cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);",
# # (10, 20, 30))
# cur.execute("DROP TABLE test")
# cur.execute(
# "CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
# cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)",
# (100, "abc'def"))
# cur.execute("SELECT * FROM test;")
# resp = cur.fetchone()
# conn.commit()
# cur.close()
# conn.close()


if __name__ == '__main__':
unittest.main()
3 changes: 2 additions & 1 deletion plugins/PY/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ SQLAlchemy==2.0.23
starlette-context==0.3.6
mysqlclient==2.2.0
grpcio
grpc-interceptor==0.15.4
grpc-interceptor==0.15.4
psycopg2
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
Path(cwd, './common/src')]

setup(name='pinpointPy',
version="1.2.0", # don't forget update __version__ in pinpointPy/__init__.py
version="1.2.1", # don't forget update __version__ in pinpointPy/__init__.py
author="cd_pinpoint members",
author_email='dl_cd_pinpoint@navercorp.com',
license='Apache License 2.0',
Expand Down
2 changes: 1 addition & 1 deletion setup_pypi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
Path(cwd, './common/src')]

setup(name='pinpointPy',
version="1.3.0", # don't forget update __version__ in pinpointPy/__init__.py
version="1.3.1", # don't forget update __version__ in pinpointPy/__init__.py
author="cd_pinpoint members",
author_email='dl_cd_pinpoint@navercorp.com',
license='Apache License 2.0',
Expand Down
28 changes: 24 additions & 4 deletions testapps/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ services:
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
dev-collector:
condition: service_started
restart: always
build:
dockerfile: testapps/django.dockerfile
Expand Down Expand Up @@ -202,6 +206,8 @@ services:
condition: service_healthy
mongodb:
condition: service_healthy
postgres:
condition: service_healthy

grpc-py:
container_name: grpc-py
Expand All @@ -226,7 +232,21 @@ services:
build:
dockerfile: testapps/grpc_py_client.dockerfile
context: ../
# healthcheck:
# test: python /app/route_guide_client.py
# interval: 5s
# timeout: 1s

postgres:
image: postgres:14-alpine
ports:
- 5432:5432
environment:
- POSTGRES_PASSWORD=pinpoint
- POSTGRES_USER=test
- POSTGRES_DB=test
# volumes:
# - ./init.sql:/docker-entrypoint-initdb.d/init.sql
restart: always
healthcheck:
test: [ "CMD-SHELL", "pg_isready", "-d", "db_prod" ]
interval: 3s
timeout: 30s
retries: 5
start_period: 80s
3 changes: 2 additions & 1 deletion testapps/django/mysite/polls/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
urlpatterns = [
path("", views.index, name="index"),
path('products/<int:pk>/', views.get_products, name="index"),
path('call/', views.get_url,name="remote")
path('call/', views.get_url,name="remote"),
path('call_postgres/',views.get_postgres,name="postgres")
]
25 changes: 23 additions & 2 deletions testapps/django/mysite/polls/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.http import HttpResponse
from django.http.request import HttpRequest
import requests
import psycopg2


def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
Expand All @@ -10,6 +12,25 @@ def get_products(request, pk):
assert isinstance(request, HttpRequest)
return HttpResponse(f"product: {pk}")


def get_url(request):
h1 =requests.get("http://testapp-php/")
return HttpResponse(h1.content)
h1 = requests.get("http://testapp-php/")
return HttpResponse(h1.content)


def get_postgres(request) -> HttpResponse:
# ref to https://www.psycopg.org/docs/usage.html
conn = psycopg2.connect(dbname="test", user="test",
password="pinpoint", host="10.34.130.156", port=5432)
cur = conn.cursor()
cur.execute("DROP TABLE IF EXISTS test")
cur.execute(
"CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)",
(100, "abc'def"))
cur.execute("SELECT * FROM test;")
resp = cur.fetchone()
conn.commit()
cur.close()
conn.close()
return HttpResponse(resp)
3 changes: 2 additions & 1 deletion testapps/django/mysite/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Django==4.1.13
mysqlclient
requests
requests
psycopg2-binary
1 change: 1 addition & 0 deletions testapps/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE DATABASE test;

0 comments on commit b851e34

Please sign in to comment.