Skip to content

Commit d1fac91

Browse files
Oleg Chaplashkinylobankov
Oleg Chaplashkin
authored andcommitted
Add a basic logging to the service
Added a basic logging and configuration of loggers. Request processing is related to the cross-service `X-GitHub-Delivery`. Resolves #28
1 parent c2b0c51 commit d1fac91

File tree

6 files changed

+135
-53
lines changed

6 files changed

+135
-53
lines changed

docbot/app.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
import logging
2+
13
from elasticapm.contrib.flask import ElasticAPM
24
from prometheus_flask_exporter import PrometheusMetrics
35
from flask import Flask, request
46

57
from .handlers import webhook_handler, list_events_handler
8+
from .logging_config import LOGGING_CONFIG
9+
10+
11+
logging.config.dictConfig(LOGGING_CONFIG)
12+
13+
log = logging.getLogger(__name__)
614

715

816
app = Flask(__name__)
917

1018
app.config['ELASTIC_APM'] = {
1119
'SERVICE_NAME': 'docbot',
1220
}
13-
apm = ElasticAPM(app)
21+
apm = ElasticAPM(app, logging=True)
1422

1523
metrics = PrometheusMetrics(app)
1624

@@ -28,4 +36,8 @@ def index() -> str:
2836
def webhook() -> str:
2937
data: dict = request.json
3038
event: str = request.headers.get('X-GitHub-Event')
31-
return webhook_handler(data, event)
39+
extra = {
40+
'X-GitHub-Delivery': request.headers.get('X-GitHub-Delivery')
41+
}
42+
log.info('Accepted webhook from GitHub service', extra=extra)
43+
return webhook_handler(data, event, extra)

docbot/github.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,47 @@
1+
import logging
2+
13
import requests
24

35
from .metrics import Metrics
46
from .utils import create_event
57

68

9+
log = logging.getLogger(__name__)
10+
11+
712
class GitHub:
813
def __init__(self, token: str) -> None:
914
self.token: str = token
1015

11-
def send_comment(self, body, issue_api, to):
16+
def send_comment(self, body, issue_api, to, extra):
1217
create_event(to, 'send_comment', body)
1318
body = {'body': '@{}: {}'.format(to, body)}
1419
url = '{}/comments'.format(issue_api)
15-
status_code = self._send_request(url, body)
16-
print('Sent comment: {}'.format(status_code))
20+
self._send_request(url, body)
21+
log.info('Sent comment', extra=extra)
1722

18-
def get_comments(self, issue_api) -> dict:
23+
def get_comments(self, issue_api, extra) -> dict:
1924
body = {'since': '1970-01-01T00:00:00Z'}
2025
url = '{}/comments'.format(issue_api)
2126
r = self._send_request(url, body, method='get')
27+
log.info('Received comments', extra=extra)
2228
return r.json()
2329

24-
def create_issue(self, title, description, src_url, author, doc_repo_url):
30+
def create_issue(self, title, description, src_url, author, doc_repo_url, extra):
2531
create_event(author, 'create_issue', title)
2632
description = '{}\nRequested by @{} in {}.'.format(description, author,
2733
src_url)
2834
body = {'title': title, 'body': description}
2935
url = '{}/issues'.format(doc_repo_url)
30-
status_code = self._send_request(url, body)
31-
print('Created issue: {}'.format(status_code))
36+
self._send_request(url, body)
37+
log.info('Created issue', extra=extra)
3238

3339
@Metrics.github_latency.time()
3440
def _send_request(self, url, body, method='post'):
3541
headers = {
3642
'Authorization': 'token {}'.format(self.token),
3743
}
3844
r = requests.request(method, url, json=body, headers=headers)
39-
print(r.status_code)
45+
log.info('Response status code: %s', r.status_code)
4046
r.raise_for_status()
4147
return r

docbot/handlers.py

+43-30
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,54 @@
1+
import logging
2+
13
from .utils import last_events
24
from . import settings
35
from .processors import process_issue_comment, process_issue_state_change, \
46
process_commit
57

68

7-
def webhook_handler(data: dict, event: str) -> str:
8-
if event is None or data is None:
9-
return 'Event or data was not found'
9+
log = logging.getLogger(__name__)
1010

11-
if 'issue' in data:
12-
issue = data['issue']
13-
if 'state' not in issue:
14-
return 'Event is not needed.'
15-
issue_state = issue['state']
16-
issue_api = issue['url']
17-
issue_url = issue['html_url']
18-
issue_repo = data['repository']['full_name']
19-
20-
if event == 'issue_comment':
21-
return process_issue_comment(data, issue_state,
22-
issue_api)
23-
elif event == 'issues':
24-
doc_repo_url = settings.doc_repo_urls.get(issue_repo)
25-
return process_issue_state_change(data, issue_api,
26-
issue_url, doc_repo_url)
11+
12+
def webhook_handler(data: dict, event: str, extra: dict) -> str:
13+
try:
14+
log.info('Start <%s> event processing', event, extra=extra)
15+
if event is None or data is None:
16+
return 'Event or data was not found'
17+
18+
if 'issue' in data:
19+
issue = data['issue']
20+
if 'state' not in issue:
21+
return 'Event is not needed.'
22+
issue_state = issue['state']
23+
issue_api = issue['url']
24+
issue_url = issue['html_url']
25+
issue_repo = data['repository']['full_name']
26+
27+
if event == 'issue_comment':
28+
return process_issue_comment(data, issue_state,
29+
issue_api, extra)
30+
elif event == 'issues':
31+
doc_repo_url = settings.doc_repo_urls.get(issue_repo)
32+
return process_issue_state_change(data, issue_api,
33+
issue_url, doc_repo_url, extra)
34+
else:
35+
return 'Event "{}" is not needed.'.format(event)
36+
elif event == 'push':
37+
repo = data['repository']
38+
branch = '/'.join(data['ref'].split('/')[2:])
39+
is_master_push = repo['master_branch'] == branch
40+
issue_repo = settings.doc_repo_urls.get(repo['full_name'])
41+
for c in data['commits']:
42+
process_commit(c, is_master_push, issue_repo, extra)
43+
return 'Webhook was processed'
2744
else:
28-
return 'Event "{}" is not needed.'.format(event)
29-
elif event == 'push':
30-
repo = data['repository']
31-
branch = '/'.join(data['ref'].split('/')[2:])
32-
is_master_push = repo['master_branch'] == branch
33-
issue_repo = settings.doc_repo_urls.get(repo['full_name'])
34-
for c in data['commits']:
35-
process_commit(c, is_master_push, issue_repo)
36-
return 'Webhook was processed'
37-
else:
38-
return 'Event is not needed.'
45+
return 'Event is not needed.'
46+
except Exception as e:
47+
log.error(
48+
'Webhook processing error: %s', e, exc_info=True, extra=extra
49+
)
50+
return str(e)
51+
3952

4053

4154
def list_events_handler() -> str:

docbot/logging_config.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from dataclasses import dataclass
2+
import ecs_logging
3+
4+
5+
LOGGING_CONFIG = {
6+
'version': 1,
7+
'disable_existing_loggers': False,
8+
'formatters': {
9+
'ecs': {
10+
'()': ecs_logging.StdlibFormatter
11+
},
12+
'default': {
13+
'format': '[%(asctime)s] - %(message)s',
14+
}
15+
},
16+
'handlers': {
17+
'ecs-stdout': {
18+
'class': 'logging.StreamHandler',
19+
'formatter': 'ecs'
20+
}
21+
},
22+
'loggers': {
23+
'': {
24+
'level': 'INFO',
25+
'handlers': ['ecs-stdout'],
26+
},
27+
'elasticapm': {
28+
'level': 'WARN',
29+
'handlers': ['ecs-stdout'],
30+
},
31+
'urllib3.connectionpool': {
32+
'level': 'ERROR',
33+
'handlers': ['ecs-stdout'],
34+
},
35+
}
36+
}

docbot/processors.py

+27-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import logging
2+
13
from . import settings
24
from .github import GitHub
35
from .utils import create_event
46

7+
8+
log = logging.getLogger(__name__)
9+
510
github = GitHub(settings.token)
611

712

@@ -25,42 +30,48 @@ def parse_comment(body):
2530
'description': body[pos:]}, None
2631

2732

28-
def process_issue_comment(body, issue_state, issue_api) -> str:
33+
def process_issue_comment(body, issue_state, issue_api, extra) -> str:
2934
action = body['action']
3035
if (action != 'created' and action != 'edited') or \
3136
issue_state != 'open':
37+
log.info('Not needed', extra=extra)
3238
return 'Not needed.'
3339
comment = body['comment']
3440
author = comment['user']['login']
3541
comment, error = parse_comment(comment['body'])
3642
if error:
37-
print('Error during request processing: {}'.format(error))
38-
github.send_comment(error, issue_api, author)
43+
log.error('Error during request processing: %s', error, extra=extra)
44+
github.send_comment(error, issue_api, author, extra)
3945
elif comment:
40-
print('Request is processed ok')
46+
log.info('Request is processed ok', extra=extra)
4147
if action == 'edited':
42-
github.send_comment('Accept edited.', issue_api, author)
48+
github.send_comment('Accept edited.', issue_api, author, extra)
4349
else:
44-
github.send_comment('Accept.', issue_api, author)
50+
github.send_comment('Accept.', issue_api, author, extra)
4551
else:
46-
print('Ignore non-request comments')
52+
log.debug('Ignore non-request comments', extra=extra)
53+
log.info('Doc request is processed', extra=extra)
4754
return 'Doc request is processed.'
4855

4956

50-
def process_issue_state_change(body, issue_api, issue_url, doc_repo_url):
57+
def process_issue_state_change(body, issue_api, issue_url, doc_repo_url, extra):
5158
action = body['action']
5259
if action != 'closed':
60+
log.info('Not needed', extra=extra)
5361
return 'Not needed.'
54-
comments = github.get_comments(issue_api)
62+
comments = github.get_comments(issue_api, extra)
5563
for c in comments:
5664
comment, error = parse_comment(c['body'])
65+
if error:
66+
log.error('Error during request processing: %s', error, extra=extra)
5767
if comment:
5868
github.create_issue(comment["title"], comment["description"],
59-
issue_url, c['user']['login'], doc_repo_url)
69+
issue_url, c['user']['login'], doc_repo_url, extra)
70+
log.info('Issue %s is processed', comment["title"], extra=extra)
6071
return 'Issue is processed.'
6172

6273

63-
def process_commit(c, is_master_push, doc_repo_url):
74+
def process_commit(c, is_master_push, doc_repo_url, extra):
6475
body = c['message']
6576
request_pos = body.find(settings.bot_name)
6677
if request_pos == -1:
@@ -69,11 +80,14 @@ def process_commit(c, is_master_push, doc_repo_url):
6980
author = c['author']['username']
7081
comment, error = parse_comment(request)
7182
if error:
72-
print('Error during request processing: {}'.format(error))
83+
log.error('Error during request processing: %s', error, extra=extra)
7384
create_event(author, 'process_commit', error)
7485
else:
7586
create_event(author, 'process_commit', 'Accept')
7687
if is_master_push:
88+
log.info(
89+
'Trying to create issue on Github %s', comment['title'], extra=extra
90+
)
7791
github.create_issue(comment['title'],
7892
comment['description'], c['url'],
79-
author, doc_repo_url)
93+
author, doc_repo_url, extra)

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ blinker==1.4
22
certifi==2022.6.15
33
charset-normalizer==2.0.12
44
click==8.1.3
5+
ecs-logging==2.0.0
56
elastic-apm==6.9.1
67
Flask==2.1.2
78
gunicorn==20.1.0

0 commit comments

Comments
 (0)