Repository navigation
BB2-4572/4911/5193: ITS-log initial integration #1746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JamesDemeryNava
wants to merge
80
commits into
master
Choose a base branch
from
jamesdemery/bb2-4572-its-log-poc
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
80 commits
Select commit
Hold shift + click to select a range
09221fb
initial work on POC
JamesDemeryNava 7f9da8b
More work on ITS-log POC, wire up logging
JamesDemeryNava b790086
Still a WIP - some logs not coming through, midleware disabled for now
JamesDemeryNava f036201
More progress
JamesDemeryNava 0604008
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava a638f62
Get django command going - hacky for now
JamesDemeryNava 1459c1f
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava bd0cb0d
Make sure more fields make it, modify log_global_state_metrics
JamesDemeryNava bda4acc
Rebase and fix conflicts
JamesDemeryNava b5be7a8
Modify API key and call its/log with cluster/break down log event int…
JamesDemeryNava f6bd001
Remove middleware and skip some log types
JamesDemeryNava fa1e234
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 2ceffcc
Rebase and fix conflicts
JamesDemeryNava 34a85df
Modify how auth_app_id is handled
JamesDemeryNava 83ae8f8
post log event in one call, minor changes around app data
JamesDemeryNava 390deee
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 2192871
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 55a1406
Small changes to its-log calls
JamesDemeryNava b3f88e5
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 3137327
Changes to post directly to summary endpoint for log_global_state_met…
JamesDemeryNava cccc77c
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 33d48bd
Updates to post all fields in log_global_state_metrics, some global l…
JamesDemeryNava f34f3fc
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 26ee8d4
Updates to how we post to summary endpoint from log_global_state_metrics
JamesDemeryNava 6d4a0ac
Temp changes
JamesDemeryNava d8c2a7c
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 25043be
Remove two app level fields, modify a global key
JamesDemeryNava 0a11d5b
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava ca3a918
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava afff2e5
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 79dc0ae
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava e47b898
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 2876b2b
Rebase and fix conflicts:
JamesDemeryNava a3d1d0a
Add some metric tracking for recently added metrics
JamesDemeryNava 54744a3
Changes to ensure all recently added metrics make it through, some cl…
JamesDemeryNava abb41fc
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava a902472
Clean up and modify a unit test
JamesDemeryNava 70bf8a0
Add unit tests for its-log
JamesDemeryNava 8321bd8
Add more tests, other cleanup
JamesDemeryNava 3ec0c4c
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava bdd5824
Rebase and fix conflicts
JamesDemeryNava b7241cb
Remove waffle switch, control its-log pinging with env var instead, m…
JamesDemeryNava 4ede72f
Add doc strings, finish conversion to env var from waffle switch
JamesDemeryNava f28aad6
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 56d509f
Remove its_log_api from performance logger
JamesDemeryNava 7e2453d
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava a444f77
Modify some tests and address PR feedback
JamesDemeryNava 63a2634
Add DJANGO_SECRET_KEY for base_local test
JamesDemeryNava e0e7da7
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
annamontare-nava 8630e07
Address PR feedback - update test comments
JamesDemeryNava c22ab91
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava b92189c
Add ThreadPoolExecutor with max_workers=10, modify a unit test
JamesDemeryNava 34c3aea
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 56c6d3e
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava b076fce
Rebase and fix conflicts
JamesDemeryNava fa1d5ed
Rebase and fix conflict
JamesDemeryNava e5b5c44
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 22cd036
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 3717bc7
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 003de72
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava b550489
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava aa3ee34
Rebase and fix conflicts
JamesDemeryNava d09ffe0
Initial steps to add BB ETLs to BB codebase
JamesDemeryNava a5b0709
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 699f8fd
Implement script for creating itslog_etl records
JamesDemeryNava 52c08cb
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 50aa221
Rebase and fix conflict'
JamesDemeryNava 4ca5a6b
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 3a1f983
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava de2ec59
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 0d7997f
Remove db-to-csv.go, modify starlark scripts, update ETL creation script
JamesDemeryNava 7c44352
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava c5abbc3
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava c9c901a
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 9a7acbd
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 5db8862
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
JamesDemeryNava 6616e8e
Reference its_log env var rather than hard coded API keys
JamesDemeryNava 53f7768
Add its_log key retrieval to base_ec2, modify starlark ETLs
JamesDemeryNava d5fad5f
Update script to address PR feedback
JamesDemeryNava 5278076
Merge branch 'master' into jamesdemery/bb2-4572-its-log-poc
bwang-icf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| import json | ||
| import logging | ||
| from concurrent.futures import ThreadPoolExecutor | ||
| from typing import Any, Dict | ||
|
|
||
| import requests | ||
| from django.conf import settings | ||
|
|
||
| from apps.logging.constants import HHS_SERVER_LOGNAME_FMT | ||
|
|
||
| logger = logging.getLogger(HHS_SERVER_LOGNAME_FMT.format(__name__)) | ||
|
|
||
| TYPES_TO_SKIP = [ | ||
| 'fhir_post_fetch', | ||
| 'fhir_pre_fetch', | ||
| 'fhir_auth_post_fetch', | ||
| 'global_state_metrics', | ||
| 'global_state_metrics_per_app', | ||
| ] | ||
| # Other possibilities: | ||
| # auth_app_data_access_type, auth_app_id, path, req_grant_Type, | ||
| ACCEPTED_LOG_KEYS = [ | ||
| 'app_name', | ||
| 'app_id', | ||
| 'type', | ||
| 'response_code', | ||
| 'fhir_id_v2', | ||
| 'fhir_id_v3', | ||
| 'allow', | ||
| 'auth_status', | ||
| 'req_grant_type', | ||
| 'auth_require_demographic_scopes', | ||
| 'share_demographic_scopes', | ||
| 'path', | ||
| 'request_method', | ||
| 'auth_grant_type', | ||
| 'action', | ||
| 'sls_userinfo_status_code', | ||
| 'auth_crosswalk_action', | ||
| 'crosswalk_fhir_id', | ||
| 'auth_path', | ||
| 'patient_match_found', | ||
| 'csp', | ||
| 'patient', | ||
|
JamesDemeryNava marked this conversation as resolved.
Resolved
|
||
| 'req_qparam_lastupdated', | ||
| 'req_qparam__source', | ||
| 'req_qparam__tag', | ||
| 'auth_share_samhsa_data', | ||
| # 'auth_app_id', | ||
| # 'auth_app_name', | ||
| ] | ||
| GRAB_FHIR_ID_FROM_USER_CROSSWALK = ['Authentication:success', 'Authorization'] | ||
| GRAB_FHIR_ID_FROM_CROSSWALK = ['AccessToken'] | ||
|
|
||
|
|
||
| class ITSLogAPIHandler(logging.Handler): | ||
| """ | ||
| Custom logging handler that forwards log records to the ITS Log API. | ||
| Fires asynchronously so it never blocks the main thread. | ||
| """ | ||
|
|
||
| API_URL = 'http://host.docker.internal:8888/v1/log/create' | ||
| API_KEY = settings.ITS_LOG_API_KEY | ||
| executor = ThreadPoolExecutor(max_workers=10, thread_name_prefix='its-log') | ||
|
|
||
| def emit(self, record) -> None: | ||
| """Format the log message if it is a log type we want to post to its-log, | ||
| then send the log event to its-log. There is no check on a env var or waffle switch | ||
| here as the ITS_LOG_API_ENABLED env var controls in base_local.py and base_ec2.py determines | ||
| if we ever hit this function. | ||
|
|
||
| Args: | ||
| record: Log record being checked and formatted for posting to its-log | ||
| """ | ||
| log_message = self._parse_log_message(record.__dict__) | ||
| if ( | ||
| log_message.get('type') in TYPES_TO_SKIP | ||
| or 'testclient' in log_message.get('path', '') | ||
| or 'admin' in log_message.get('path', '') | ||
| ): | ||
| return | ||
|
|
||
| updated_log_message = self._format_log_message(log_message) | ||
| if not updated_log_message: | ||
| return | ||
| payload = self._build_payload(updated_log_message) | ||
|
|
||
| self.executor.submit(self._post_to_api, payload) | ||
|
|
||
| def _parse_log_message(self, record) -> Dict[str, Any]: | ||
| """Extracts the msg attribute of the log record, checks that value is a string that starts with | ||
| { or [, and returns the dictionary typed value of the msg attribute | ||
|
|
||
| Args: | ||
| record: Log record being checked and formatted for posting to its-log | ||
|
|
||
| Returns: | ||
| Dict[str, Any]: The dictionary typed vale of the log record's msg attribute | ||
| """ | ||
| msg = record.get('msg', '') | ||
|
|
||
| # Only attempt JSON parsing if it looks like a JSON object or array | ||
| if isinstance(msg, str) and msg.strip().startswith(('{', '[')): | ||
| try: | ||
| return json.loads(msg) | ||
| except (json.JSONDecodeError, ValueError): | ||
| pass | ||
|
|
||
| # Return None (or the raw string) if it's not valid JSON | ||
| return {} | ||
|
|
||
| def _build_payload(self, log_message) -> Dict[str, Any]: | ||
| """Builds a paylod for the its-log API events endpoint. Tags is currently just an empty | ||
| list, could be removed or modified in the future. We only want certain log attributes in | ||
| the value column of itslog_events, so we filter for the specific log attributes that we want. | ||
|
|
||
| Args: | ||
| log_message (dict): Dictionary containing the details of the log event | ||
|
|
||
| Returns: | ||
| Dict[str, Any]: Formatted payload that the its-log API event endpoint expects | ||
| """ | ||
| tags = [] | ||
| filtered_log = {k: v for k, v in log_message.items() if k in ACCEPTED_LOG_KEYS} | ||
|
|
||
| return { | ||
| 'tags': tags, | ||
| 'value': json.dumps(filtered_log), | ||
| 'type': 'text', | ||
| } | ||
|
|
||
| def _format_log_message(self, log_message: Dict[str, Any]) -> Dict[str, Any]: | ||
| """Format the log_message before building the its-log API payload, so the payload | ||
| sent to its-log will have the fields that the different ETL steps are looking for | ||
| to calculate metrics | ||
|
|
||
| Args: | ||
| log_message (Dict[str, Any]): Dictionary containing the details of the log event | ||
|
|
||
| Returns: | ||
| Dict[str, Any]: Formatted log message with specific keys that are looked for in our data pipeline | ||
| """ | ||
| if log_message.get('type'): | ||
| if log_message.get('type') in GRAB_FHIR_ID_FROM_USER_CROSSWALK: | ||
| log_message['fhir_id_v2'] = log_message.get('user').get('crosswalk').get('fhir_id_v2') | ||
| log_message['fhir_id_v3'] = log_message.get('user').get('crosswalk').get('fhir_id_v3') | ||
| elif log_message.get('type') in GRAB_FHIR_ID_FROM_CROSSWALK: | ||
| log_message['fhir_id_v2'] = log_message.get('crosswalk').get('fhir_id') | ||
| if log_message.get('code'): | ||
| log_message['response_code'] = log_message.get('code') | ||
| if ( | ||
| '?' in log_message.get('location', '') | ||
| and 'authorize' in log_message.get('location', '') | ||
| and log_message.get('location', '').startswith('/v') | ||
| ): | ||
| log_message['auth_path'] = log_message.get('location').split('?')[0] | ||
|
|
||
| if log_message.get('req_grant_type'): | ||
| log_message['auth_grant_type'] = log_message.get('req_grant_type') | ||
|
|
||
| if not log_message.get('app_id') and log_message.get('auth_app_id'): | ||
| log_message['app_id'] = log_message.get('auth_app_id') | ||
| if not log_message.get('app_name') and log_message.get('auth_app_name'): | ||
| log_message['app_name'] = log_message.get('auth_app_name') | ||
| if not log_message.get('app_id') and log_message.get('resp_app_id'): | ||
| log_message['app_id'] = log_message.get('resp_app_id') | ||
| if not log_message.get('app_name') and log_message.get('resp_app_name'): | ||
| log_message['app_name'] = log_message.get('resp_app_name') | ||
| if not log_message.get('app_id') and log_message.get('req_app_id'): | ||
| log_message['app_id'] = log_message.get('req_app_id') | ||
| if not log_message.get('app_name') and log_message.get('req_app_name'): | ||
| log_message['app_name'] = log_message.get('req_app_name') | ||
| if not log_message.get('app_id') and log_message.get('application'): | ||
| log_message['app_id'] = log_message['application'].get('id') | ||
| log_message['app_name'] = log_message['application'].get('name') | ||
|
|
||
| if 'allow' in log_message: | ||
| log_message['allow'] = 'True' if log_message.get('allow') is True else 'False' | ||
|
|
||
| return log_message | ||
|
|
||
| def _post_to_api(self, payload) -> None: | ||
| """Post the given payload to the its-log API events endpoint | ||
|
|
||
| Args: | ||
| payload (dict): The payload being posted to the its-log API | ||
| """ | ||
| logger.info(f'Payload for its-log: {json.dumps(payload)}') | ||
| try: | ||
| requests.post(self.API_URL, headers={'x-api-key': self.API_KEY}, json=payload, timeout=5) | ||
| logger.info('Successfully posted to its-log') | ||
| except Exception: | ||
| # Do not let ITS-log failures crash BlueButton | ||
| logger.error('Failed to post to its-log API') | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.