logtopg 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logtopg/__init__.py +265 -0
- logtopg/createtable.sql +40 -0
- logtopg/insertrow.sql +36 -0
- logtopg/tests/__init__.py +0 -0
- logtopg/tests/test_logtopg.py +278 -0
- logtopg/version.py +25 -0
- logtopg-1.0.2.dist-info/METADATA +15 -0
- logtopg-1.0.2.dist-info/RECORD +10 -0
- logtopg-1.0.2.dist-info/WHEEL +5 -0
- logtopg-1.0.2.dist-info/top_level.txt +1 -0
logtopg/__init__.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# vim: set expandtab ts=4 sw=4 filetype=python fileencoding=utf8:
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import textwrap
|
|
8
|
+
import traceback
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
import psutil
|
|
12
|
+
|
|
13
|
+
import pkg_resources
|
|
14
|
+
import psycopg2
|
|
15
|
+
from psycopg2.extensions import adapt
|
|
16
|
+
|
|
17
|
+
from logtopg.version import __version__
|
|
18
|
+
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class PGHandler(logging.Handler):
|
|
22
|
+
|
|
23
|
+
def __init__(self, log_table_name,
|
|
24
|
+
database,
|
|
25
|
+
user=None,
|
|
26
|
+
password=None,
|
|
27
|
+
host=None,
|
|
28
|
+
port=5432):
|
|
29
|
+
|
|
30
|
+
logging.Handler.__init__(self)
|
|
31
|
+
|
|
32
|
+
self.log_table_name = log_table_name
|
|
33
|
+
|
|
34
|
+
self.database = database
|
|
35
|
+
self.host = host
|
|
36
|
+
self.user = user
|
|
37
|
+
self.password = password
|
|
38
|
+
self.port = port
|
|
39
|
+
|
|
40
|
+
self.pgconn = None
|
|
41
|
+
self.create_table_sql = None
|
|
42
|
+
self.insert_row_sql = None
|
|
43
|
+
|
|
44
|
+
def check_if_log_table_exists(self):
|
|
45
|
+
|
|
46
|
+
while (True):
|
|
47
|
+
pgconn = self.get_pgconn()
|
|
48
|
+
|
|
49
|
+
cursor = pgconn.cursor()
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
cursor.execute("""
|
|
53
|
+
SELECT %s::regclass;
|
|
54
|
+
""", [self.log_table_name])
|
|
55
|
+
except psycopg2.ProgrammingError as e:
|
|
56
|
+
return False
|
|
57
|
+
except InterfaceError as ie:
|
|
58
|
+
self.pgconn = None
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
def maybe_create_table(self):
|
|
64
|
+
|
|
65
|
+
if not self.check_if_log_table_exists():
|
|
66
|
+
|
|
67
|
+
create_table_sql = self.get_create_table_sql()
|
|
68
|
+
|
|
69
|
+
out = run_sql_commands(create_table_sql, self.user, self.password,
|
|
70
|
+
self.host, self.port, self.database)
|
|
71
|
+
|
|
72
|
+
log.info("Created log table {0}.".format(self.log_table_name))
|
|
73
|
+
|
|
74
|
+
def get_pgconn(self):
|
|
75
|
+
|
|
76
|
+
if not self.pgconn:
|
|
77
|
+
self.make_pgconn()
|
|
78
|
+
|
|
79
|
+
return self.pgconn
|
|
80
|
+
|
|
81
|
+
def make_pgconn(self):
|
|
82
|
+
|
|
83
|
+
self.pgconn = psycopg2.connect(
|
|
84
|
+
database=self.database,
|
|
85
|
+
host=self.host,
|
|
86
|
+
user=self.user,
|
|
87
|
+
password=self.password,
|
|
88
|
+
port=self.port)
|
|
89
|
+
|
|
90
|
+
self.pgconn.autocommit = True
|
|
91
|
+
|
|
92
|
+
log.info("Just made an autocommitting database connection: {0}.".format(
|
|
93
|
+
self.pgconn))
|
|
94
|
+
|
|
95
|
+
def get_create_table_sql(self):
|
|
96
|
+
|
|
97
|
+
if not self.create_table_sql:
|
|
98
|
+
|
|
99
|
+
s = \
|
|
100
|
+
pkg_resources.resource_string(
|
|
101
|
+
"logtopg", "createtable.sql")\
|
|
102
|
+
.decode("utf-8")\
|
|
103
|
+
.format(self.log_table_name)
|
|
104
|
+
|
|
105
|
+
self.create_table_sql = s.encode("utf-8")
|
|
106
|
+
|
|
107
|
+
return self.create_table_sql
|
|
108
|
+
|
|
109
|
+
def get_insert_row_sql(self):
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
Cache the insert query (with placeholder parameters) in memory
|
|
113
|
+
so that every log.... call doesn't do file IO.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
if not self.insert_row_sql:
|
|
117
|
+
|
|
118
|
+
self.insert_row_sql = \
|
|
119
|
+
pkg_resources.resource_string(
|
|
120
|
+
"logtopg", "insertrow.sql")\
|
|
121
|
+
.decode("utf-8")\
|
|
122
|
+
.format(self.log_table_name)
|
|
123
|
+
|
|
124
|
+
return self.insert_row_sql
|
|
125
|
+
|
|
126
|
+
def build_d(self, record_dict):
|
|
127
|
+
|
|
128
|
+
d = record_dict
|
|
129
|
+
|
|
130
|
+
# Insert process info
|
|
131
|
+
d['cmd_line'] = " ".join(psutil.Process(os.getpid()).cmdline())
|
|
132
|
+
|
|
133
|
+
# Catch messages that can't be adapted as-is, and convert it to
|
|
134
|
+
# strings
|
|
135
|
+
try:
|
|
136
|
+
d["msg"] = adapt(record_dict["msg"])
|
|
137
|
+
|
|
138
|
+
except Exception as ex:
|
|
139
|
+
d["msg"] = str(record_dict["msg"])
|
|
140
|
+
|
|
141
|
+
return d
|
|
142
|
+
|
|
143
|
+
def emit(self, record):
|
|
144
|
+
|
|
145
|
+
self.format(record)
|
|
146
|
+
|
|
147
|
+
if record.exc_info:
|
|
148
|
+
record.exc_text = logging._defaultFormatter.formatException(record.exc_info)
|
|
149
|
+
|
|
150
|
+
else:
|
|
151
|
+
record.exc_text = ""
|
|
152
|
+
|
|
153
|
+
if isinstance(record.msg, Exception):
|
|
154
|
+
record.msg = str(record.msg)
|
|
155
|
+
|
|
156
|
+
pgconn = self.get_pgconn()
|
|
157
|
+
|
|
158
|
+
self.maybe_create_table()
|
|
159
|
+
|
|
160
|
+
cursor = pgconn.cursor()
|
|
161
|
+
|
|
162
|
+
cursor.execute(
|
|
163
|
+
self.get_insert_row_sql(),
|
|
164
|
+
self.build_d(record.__dict__))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
example_dict_config = dict({
|
|
168
|
+
|
|
169
|
+
"loggers": {
|
|
170
|
+
"logtopg": {
|
|
171
|
+
# "handlers": ["pg", "console"],
|
|
172
|
+
"handlers": ["console"],
|
|
173
|
+
"level": "DEBUG",
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
'handlers': {
|
|
178
|
+
'pg': {
|
|
179
|
+
'class': 'logtopg.PGHandler',
|
|
180
|
+
'level': 'DEBUG',
|
|
181
|
+
'log_table_name': 'logtopg_logs',
|
|
182
|
+
|
|
183
|
+
"database":"logtopg",
|
|
184
|
+
"host":"localhost",
|
|
185
|
+
"user":"logtopg",
|
|
186
|
+
"password":"l0gt0pg",
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
"console": {
|
|
190
|
+
"class": "logging.StreamHandler",
|
|
191
|
+
"level": "DEBUG",
|
|
192
|
+
"formatter": "consolefmt",
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
"formatters": {
|
|
198
|
+
"consolefmt":{
|
|
199
|
+
"format": '%(asctime)-22s [%(process)d] %(name)-30s %(lineno)-5d %(levelname)-8s %(message)s',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
# Any handlers attached to root get log messages from EVERYTHING,
|
|
204
|
+
# like third-party modules, etc.
|
|
205
|
+
'root': {
|
|
206
|
+
'handlers': ["pg"],
|
|
207
|
+
'level': 'DEBUG',
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
'version': 1,
|
|
211
|
+
|
|
212
|
+
# This is important! Without it, any log instances created before
|
|
213
|
+
# you run logging.config.dictConfig(...) will be disabled, which
|
|
214
|
+
# means all the global log objects in all the various imported files
|
|
215
|
+
# won't do anything.
|
|
216
|
+
'disable_existing_loggers': False,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
def run_sql_commands(sql_text, user, password, host, port, database):
|
|
220
|
+
|
|
221
|
+
"""
|
|
222
|
+
Run a whole bunch of SQL commands. This is nice when you have a
|
|
223
|
+
script with more than one statement in it.
|
|
224
|
+
|
|
225
|
+
Don't pass me the path to a SQL script file! Instead, give me the
|
|
226
|
+
sql text after you read it in from a file.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
env = os.environ.copy()
|
|
230
|
+
|
|
231
|
+
if password:
|
|
232
|
+
env['PGPASSWORD'] = password
|
|
233
|
+
|
|
234
|
+
# Feed the sql_text to psql's stdin.
|
|
235
|
+
# http://stackoverflow.com/questions/163542/python-how-do-i-pass-a-string-into-subprocess-popen-using-the-stdin-argument
|
|
236
|
+
|
|
237
|
+
stuff = [
|
|
238
|
+
"psql",
|
|
239
|
+
"--quiet",
|
|
240
|
+
"--no-psqlrc",
|
|
241
|
+
"-d",
|
|
242
|
+
database,
|
|
243
|
+
"--single-transaction",
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
if user:
|
|
247
|
+
stuff.append("-U")
|
|
248
|
+
stuff.append(user)
|
|
249
|
+
|
|
250
|
+
if host:
|
|
251
|
+
stuff.append("-h")
|
|
252
|
+
stuff.append(host)
|
|
253
|
+
|
|
254
|
+
if port:
|
|
255
|
+
stuff.append("-p")
|
|
256
|
+
stuff.append(str(port))
|
|
257
|
+
|
|
258
|
+
p = subprocess.Popen(
|
|
259
|
+
stuff,
|
|
260
|
+
stdin=subprocess.PIPE,
|
|
261
|
+
env=env)
|
|
262
|
+
|
|
263
|
+
out = p.communicate(input=sql_text)
|
|
264
|
+
|
|
265
|
+
return out
|
logtopg/createtable.sql
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
create table if not exists {0} (
|
|
2
|
+
|
|
3
|
+
log_id int generated
|
|
4
|
+
by default as identity primary key,
|
|
5
|
+
|
|
6
|
+
created timestamptz,
|
|
7
|
+
|
|
8
|
+
process_id int,
|
|
9
|
+
process_name text,
|
|
10
|
+
|
|
11
|
+
logger_name ltree,
|
|
12
|
+
|
|
13
|
+
path_name text,
|
|
14
|
+
module text,
|
|
15
|
+
file_name text,
|
|
16
|
+
|
|
17
|
+
function_name text,
|
|
18
|
+
|
|
19
|
+
line_number int,
|
|
20
|
+
|
|
21
|
+
log_level text,
|
|
22
|
+
log_level_number int,
|
|
23
|
+
|
|
24
|
+
cmd_line text,
|
|
25
|
+
|
|
26
|
+
message text,
|
|
27
|
+
|
|
28
|
+
exc_info text,
|
|
29
|
+
thread_id bigint,
|
|
30
|
+
thread_name text,
|
|
31
|
+
inserted timestamptz not null default now()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
create index on {0} (created);
|
|
35
|
+
create index on {0} (inserted);
|
|
36
|
+
create index on {0} (logger_name);
|
|
37
|
+
create index on {0} (process_id);
|
|
38
|
+
|
|
39
|
+
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
40
|
+
CREATE INDEX idx_{0}_cmdline ON {0} USING GIN (cmd_line gin_trgm_ops);
|
logtopg/insertrow.sql
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
insert into {0} (
|
|
2
|
+
created,
|
|
3
|
+
process_id,
|
|
4
|
+
process_name,
|
|
5
|
+
logger_name,
|
|
6
|
+
path_name,
|
|
7
|
+
module,
|
|
8
|
+
file_name,
|
|
9
|
+
function_name,
|
|
10
|
+
line_number,
|
|
11
|
+
log_level,
|
|
12
|
+
log_level_number,
|
|
13
|
+
message,
|
|
14
|
+
exc_info,
|
|
15
|
+
cmd_line,
|
|
16
|
+
thread_id,
|
|
17
|
+
thread_name
|
|
18
|
+
) values (
|
|
19
|
+
to_timestamp(%(created)s),
|
|
20
|
+
%(process)s,
|
|
21
|
+
%(processName)s,
|
|
22
|
+
%(name)s,
|
|
23
|
+
%(pathname)s,
|
|
24
|
+
%(module)s,
|
|
25
|
+
%(filename)s,
|
|
26
|
+
%(funcName)s,
|
|
27
|
+
%(lineno)s,
|
|
28
|
+
%(levelname)s,
|
|
29
|
+
%(levelno)s,
|
|
30
|
+
%(message)s,
|
|
31
|
+
%(exc_text)s,
|
|
32
|
+
%(cmd_line)s,
|
|
33
|
+
%(thread)s,
|
|
34
|
+
%(threadName)s
|
|
35
|
+
);
|
|
36
|
+
|
|
File without changes
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# vim: set expandtab ts=4 sw=4 filetype=python fileencoding=utf8:
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import logging.config
|
|
5
|
+
import os
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
import logtopg
|
|
9
|
+
import psycopg2
|
|
10
|
+
|
|
11
|
+
testing_dict_config = dict({
|
|
12
|
+
|
|
13
|
+
"loggers": {
|
|
14
|
+
"logtopg": {
|
|
15
|
+
"handlers": ["pg"],
|
|
16
|
+
"level": "DEBUG",
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
'handlers': {
|
|
21
|
+
'pg': {
|
|
22
|
+
'class': 'logtopg.PGHandler',
|
|
23
|
+
'level': 'DEBUG',
|
|
24
|
+
'log_table_name': 'logtopg_tests',
|
|
25
|
+
"database":"logtopg_tests",
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
"console": {
|
|
29
|
+
"class": "logging.StreamHandler",
|
|
30
|
+
"level": "DEBUG",
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
# 'root': {
|
|
36
|
+
# 'handlers': ["console"],
|
|
37
|
+
# 'level': 'DEBUG'},
|
|
38
|
+
|
|
39
|
+
'version': 1,
|
|
40
|
+
|
|
41
|
+
# This is important! Without it, any log instances created before
|
|
42
|
+
# you run logging.config.dictConfig(...) will be disabled.
|
|
43
|
+
'disable_existing_loggers': False,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
class Test1(unittest.TestCase):
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
This depends on a real postgresql database. I'll create a table and
|
|
50
|
+
then drop it.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
d = testing_dict_config
|
|
54
|
+
log_table_name = d["handlers"]["pg"]["log_table_name"]
|
|
55
|
+
database = d["handlers"]["pg"]["database"]
|
|
56
|
+
user = d["handlers"]["pg"].get("user")
|
|
57
|
+
password = d["handlers"]["pg"].get("password")
|
|
58
|
+
host = d["handlers"]["pg"].get("host")
|
|
59
|
+
|
|
60
|
+
db_credentials = dict(
|
|
61
|
+
user=user,
|
|
62
|
+
password=password,
|
|
63
|
+
host=host,
|
|
64
|
+
database=database,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def setUp(self):
|
|
68
|
+
|
|
69
|
+
logging.config.dictConfig(self.d)
|
|
70
|
+
|
|
71
|
+
self.log = logging.getLogger("logtopg.tests")
|
|
72
|
+
|
|
73
|
+
self.ltpg = logtopg.PGHandler(
|
|
74
|
+
self.log_table_name,
|
|
75
|
+
self.user,
|
|
76
|
+
self.password,
|
|
77
|
+
self.host,
|
|
78
|
+
self.database)
|
|
79
|
+
|
|
80
|
+
# Make a separate database connection to check results in
|
|
81
|
+
# database.
|
|
82
|
+
self.test_pgconn = psycopg2.connect(**self.db_credentials)
|
|
83
|
+
|
|
84
|
+
def test_1(self):
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
Verify we only read sql files once each.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
self.assertTrue(self.ltpg.create_table_sql is None)
|
|
91
|
+
|
|
92
|
+
s1 = self.ltpg.get_create_table_sql()
|
|
93
|
+
|
|
94
|
+
self.assertTrue(isinstance(self.ltpg.create_table_sql, bytes))
|
|
95
|
+
|
|
96
|
+
s2 = self.ltpg.get_create_table_sql()
|
|
97
|
+
|
|
98
|
+
self.assertTrue(s1 is s2)
|
|
99
|
+
|
|
100
|
+
self.ltpg.get_insert_row_sql()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_2(self):
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
Verify we make only one database connection in an instance.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
ltpg = logtopg.PGHandler(
|
|
110
|
+
self.log_table_name,
|
|
111
|
+
**self.db_credentials)
|
|
112
|
+
|
|
113
|
+
self.assertTrue(ltpg.pgconn is None)
|
|
114
|
+
|
|
115
|
+
conn1 = ltpg.get_pgconn()
|
|
116
|
+
|
|
117
|
+
self.assertTrue(ltpg.pgconn)
|
|
118
|
+
|
|
119
|
+
conn2 = ltpg.get_pgconn()
|
|
120
|
+
|
|
121
|
+
self.assertTrue(conn1 is conn2)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_3(self):
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
Verify we can create the log table.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
ltpg = logtopg.PGHandler(
|
|
131
|
+
self.log_table_name,
|
|
132
|
+
**self.db_credentials)
|
|
133
|
+
|
|
134
|
+
ltpg.maybe_create_table()
|
|
135
|
+
|
|
136
|
+
# Now, verify the table exists.
|
|
137
|
+
cursor = ltpg.pgconn.cursor()
|
|
138
|
+
|
|
139
|
+
cursor.execute("""
|
|
140
|
+
select exists(
|
|
141
|
+
select *
|
|
142
|
+
from information_schema.tables
|
|
143
|
+
where table_name = %s)
|
|
144
|
+
""", [self.log_table_name])
|
|
145
|
+
|
|
146
|
+
row = cursor.fetchone()
|
|
147
|
+
|
|
148
|
+
self.assertTrue(row[0], )
|
|
149
|
+
|
|
150
|
+
# Subsequent calls to maybe_create_table should be harmless and
|
|
151
|
+
# nearly instantaneous.
|
|
152
|
+
ltpg.maybe_create_table()
|
|
153
|
+
ltpg.maybe_create_table()
|
|
154
|
+
ltpg.maybe_create_table()
|
|
155
|
+
|
|
156
|
+
ltpg.pgconn.rollback()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_4(self):
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
Verify log messages are stored in the database.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
logging.config.dictConfig(self.d)
|
|
166
|
+
|
|
167
|
+
log1 = logging.getLogger("logtopg.tests")
|
|
168
|
+
log2 = logging.getLogger("logtopg.tests")
|
|
169
|
+
log3 = logging.getLogger("logtopg.tests")
|
|
170
|
+
log4 = logging.getLogger("logtopg.tests")
|
|
171
|
+
|
|
172
|
+
log = logging.getLogger("logtopg.tests")
|
|
173
|
+
|
|
174
|
+
log.debug("debug!")
|
|
175
|
+
log.info("info!")
|
|
176
|
+
log.warning("warning!")
|
|
177
|
+
log.error("error!")
|
|
178
|
+
log.critical("critical!")
|
|
179
|
+
|
|
180
|
+
# Now check that those logs are actually in the database.
|
|
181
|
+
cursor = self.test_pgconn.cursor()
|
|
182
|
+
|
|
183
|
+
cursor.execute(
|
|
184
|
+
"""
|
|
185
|
+
select message
|
|
186
|
+
from {}
|
|
187
|
+
where process_id = %s
|
|
188
|
+
""".format(self.log_table_name), [os.getpid()])
|
|
189
|
+
|
|
190
|
+
counted_rows = cursor.rowcount
|
|
191
|
+
|
|
192
|
+
self.test_pgconn.rollback()
|
|
193
|
+
|
|
194
|
+
# There should be 7 logs in the database with this process's ID.
|
|
195
|
+
# Those 7 are the five above and the two connection logs.
|
|
196
|
+
self.assertEqual(counted_rows, 7)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_5(self):
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
Verify different logger instances use a single database
|
|
203
|
+
connection.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
logging.config.dictConfig(self.d)
|
|
207
|
+
|
|
208
|
+
log1 = logging.getLogger("logtopg.tests.a")
|
|
209
|
+
log1.debug("trying this guy out")
|
|
210
|
+
|
|
211
|
+
log2 = logging.getLogger("logtopg.tests.b")
|
|
212
|
+
log2.debug("trying this guy out")
|
|
213
|
+
|
|
214
|
+
def test_6(self):
|
|
215
|
+
|
|
216
|
+
"""
|
|
217
|
+
Log an exception to the database.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
logging.config.dictConfig(self.d)
|
|
221
|
+
log = logging.getLogger("logtopg.tests.tests_6")
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
|
|
225
|
+
1/0
|
|
226
|
+
|
|
227
|
+
except Exception as ex:
|
|
228
|
+
|
|
229
|
+
log.exception(ex)
|
|
230
|
+
|
|
231
|
+
log.debug(AttributeError("This is a bogus exception"))
|
|
232
|
+
|
|
233
|
+
def test_7(self):
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
Log something that can't be adapted to the database.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
logging.config.dictConfig(self.d)
|
|
240
|
+
log = logging.getLogger("logtopg.tests.tests_7")
|
|
241
|
+
|
|
242
|
+
class Unadaptable(object):
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
u = Unadaptable()
|
|
246
|
+
|
|
247
|
+
log.debug("u is a {0}.".format(u))
|
|
248
|
+
log.debug(u)
|
|
249
|
+
log.debug(dict(u=u))
|
|
250
|
+
|
|
251
|
+
def tearDown(self):
|
|
252
|
+
|
|
253
|
+
self.test_pgconn.rollback()
|
|
254
|
+
|
|
255
|
+
cursor = self.test_pgconn.cursor()
|
|
256
|
+
|
|
257
|
+
cursor.execute(
|
|
258
|
+
"drop table if exists {0}".format(
|
|
259
|
+
Test1.log_table_name))
|
|
260
|
+
|
|
261
|
+
self.test_pgconn.commit()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def tearDownModule():
|
|
265
|
+
|
|
266
|
+
pgconn = psycopg2.connect(**Test1.db_credentials)
|
|
267
|
+
|
|
268
|
+
cursor = pgconn.cursor()
|
|
269
|
+
|
|
270
|
+
cursor.execute(
|
|
271
|
+
"drop table if exists {0}".format(
|
|
272
|
+
Test1.log_table_name))
|
|
273
|
+
|
|
274
|
+
pgconn.commit()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if __name__ == "__main__":
|
|
278
|
+
unittest.main()
|
logtopg/version.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# vim: set expandtab ts=4 sw=4 filetype=python fileencoding=utf8:
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Do not do anything in this file except define the __version__ variable!
|
|
5
|
+
|
|
6
|
+
The setup.py script reads this version from here during install.
|
|
7
|
+
|
|
8
|
+
In the past, I've defined the version in the setup.py file (A), or
|
|
9
|
+
defined it in the top of the project, like in logtopg/__init__.py (B).
|
|
10
|
+
|
|
11
|
+
Choice A is bad because it isn't easy to fire up a python session and
|
|
12
|
+
then do::
|
|
13
|
+
|
|
14
|
+
>>> import logtopg
|
|
15
|
+
>>> logtopg.__version__ # doctest: +SKIP
|
|
16
|
+
|
|
17
|
+
to look up the version.
|
|
18
|
+
|
|
19
|
+
And choice B is bad because the logtopg/__init__.py file might blow up
|
|
20
|
+
during install because it tries to import a some third-party module that
|
|
21
|
+
hasn't been imported yet.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__version__ = "1.0.2"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logtopg
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Python logging handler that stores logs in postgresql
|
|
5
|
+
Home-page: https://github.com/216software/logtopg/
|
|
6
|
+
Author: 216 Software, LLC
|
|
7
|
+
Author-email: info@216software.com
|
|
8
|
+
License: BSD License
|
|
9
|
+
Requires-Dist: psycopg2
|
|
10
|
+
Dynamic: author
|
|
11
|
+
Dynamic: author-email
|
|
12
|
+
Dynamic: home-page
|
|
13
|
+
Dynamic: license
|
|
14
|
+
Dynamic: requires-dist
|
|
15
|
+
Dynamic: summary
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
logtopg/__init__.py,sha256=wEvVz_YRJcR3wTvFGQM59BtWZdEXsV2pxp9IUwBv2So,6283
|
|
2
|
+
logtopg/createtable.sql,sha256=qilEAG8B371vqGacO9Tu1v5v-dhQZ_K5LNW1Tn-zywA,744
|
|
3
|
+
logtopg/insertrow.sql,sha256=mr-3kVSMxDNfMpfGj0k2utewK81zNQtmyIlvQslKW8M,577
|
|
4
|
+
logtopg/version.py,sha256=AwDmPU8h5HA6c_izak87tlXvUQi9Z3dT5g5N7bvBhF0,722
|
|
5
|
+
logtopg/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
logtopg/tests/test_logtopg.py,sha256=rVeM-o9Ggi9CpNb5v0ovhBBoCGj-pxwrW4fG8oCzi54,6259
|
|
7
|
+
logtopg-1.0.2.dist-info/METADATA,sha256=MYkXymE_KyckIpr8KzGun4xjyt5qtM2MR5XAlX4TyFc,385
|
|
8
|
+
logtopg-1.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
logtopg-1.0.2.dist-info/top_level.txt,sha256=56AgV79tf5I8ve-ns3cXFYYFbHtjh6IbSA-mfEhvPS4,8
|
|
10
|
+
logtopg-1.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
logtopg
|