Skip to content

Commit d93b963

Browse files
committed
feat: migrate to ResilientLogger
Switch using the resilient logger's audit logging over the project's own audit log. The old model can be removed later when confirmed that the logging is functioning correctly. Refs: ATV-254
1 parent 0aeceea commit d93b963

21 files changed

Lines changed: 523 additions & 586 deletions

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ run all the formatting tools as git hooks automatically before a
117117
commit.
118118

119119

120+
## Audit logging
121+
122+
Audit logging is implemented using the [resilient-logger](https://github.com/City-of-Helsinki/resilient-logger) library.
123+
See audit_log package.
124+
125+
120126
## Running tests
121127

122128
* Set the `DEBUG` environment variable to `1`.

atv/settings.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
default_var_root = environ.Path(checkout_dir("var"))
2222

2323
env = environ.Env(
24+
# Resilient logger config
25+
AUDIT_LOG_ENVIRONMENT=(str, ""),
26+
AUDIT_LOG_ES_URL=(str, ""),
27+
AUDIT_LOG_ES_INDEX=(str, ""),
28+
AUDIT_LOG_ES_USERNAME=(str, ""),
29+
AUDIT_LOG_ES_PASSWORD=(str, ""),
2430
DEBUG=(bool, False),
2531
SECRET_KEY=(str, ""),
2632
MEDIA_ROOT=(environ.Path(), default_var_root("media")),
@@ -51,6 +57,7 @@
5157
FIELD_ENCRYPTION_KEYS=(list, []),
5258
ENABLE_AUTOMATIC_ATTACHMENT_FILE_DELETION=(bool, True),
5359
ENABLE_SWAGGER_UI=(bool, True),
60+
# Old audit log settings, TODO: remove later in ATV-252.
5461
ELASTIC_AUDIT_LOG_INDEX=(str, "logs-atv-audit"),
5562
ELASTIC_CREATE_DATA_STREAM=(bool, False),
5663
ELASTIC_HOST=(str, ""),
@@ -163,6 +170,7 @@ def sentry_traces_sampler(sampling_context: SamplingContext) -> float:
163170
"documents",
164171
"audit_log",
165172
"logger_extra",
173+
"resilient_logger",
166174
]
167175

168176
MIDDLEWARE = [
@@ -267,7 +275,7 @@ def sentry_traces_sampler(sampling_context: SamplingContext) -> float:
267275
}
268276

269277
# Audit logging
270-
278+
# Old audit log settings TODO: remove later in ATV-252.
271279
AUDIT_LOG_ORIGIN = "atv"
272280
ELASTIC_AUDIT_LOG_INDEX = env("ELASTIC_AUDIT_LOG_INDEX")
273281
ELASTIC_HOST = env("ELASTIC_HOST")
@@ -279,6 +287,32 @@ def sentry_traces_sampler(sampling_context: SamplingContext) -> float:
279287
ENABLE_SEND_AUDIT_LOG = env("ENABLE_SEND_AUDIT_LOG")
280288
CLEAR_AUDIT_LOG_ENTRIES = env("CLEAR_AUDIT_LOG_ENTRIES")
281289

290+
# Resilient logger settings
291+
RESILIENT_LOGGER = {
292+
"origin": "atv",
293+
"environment": env("AUDIT_LOG_ENVIRONMENT"),
294+
"sources": [
295+
{
296+
"class": "resilient_logger.sources.ResilientLogSource",
297+
}
298+
],
299+
"targets": [
300+
{
301+
"class": "resilient_logger.targets.ElasticsearchLogTarget",
302+
"es_url": env("AUDIT_LOG_ES_URL"),
303+
"es_username": env("AUDIT_LOG_ES_USERNAME"),
304+
"es_password": env("AUDIT_LOG_ES_PASSWORD"),
305+
"es_index": env("AUDIT_LOG_ES_INDEX"),
306+
"required": True,
307+
}
308+
],
309+
"batch_limit": 5000,
310+
"chunk_size": 500,
311+
"submit_unsent_entries": True,
312+
"clear_sent_entries": True,
313+
}
314+
315+
282316
USE_X_FORWARDED_FOR = env("USE_X_FORWARDED_FOR")
283317

284318
LOGGING = {

audit_log/README.md

Lines changed: 0 additions & 77 deletions
This file was deleted.

audit_log/admin.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

audit_log/audit_logging.py

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
import logging
12
from datetime import datetime, timezone
2-
from typing import Callable, Optional, Union
3+
from typing import Optional, Union
34

4-
from django.conf import settings
55
from django.contrib.auth import get_user_model
66
from django.contrib.auth.models import AnonymousUser
77
from django.db.models import Model
88
from django.db.models.base import ModelBase
9+
from resilient_logger.sources import ResilientLogSource
910

1011
from audit_log.enums import Operation, Role, Status
11-
from audit_log.models import AuditLogEntry
1212

1313
User = get_user_model()
1414

@@ -18,18 +18,12 @@ def _now() -> datetime:
1818
return datetime.now(tz=timezone.utc)
1919

2020

21-
def _iso8601_date(time: datetime) -> str:
22-
"""Formats the timestamp in ISO-8601 format, e.g. '2020-06-01T00:00:00.000Z'."""
23-
return f"{time.replace(tzinfo=None).isoformat(timespec='milliseconds')}Z"
24-
25-
2621
def log(
2722
actor: Optional[Union[User, AnonymousUser]],
2823
actor_backend: str,
2924
operation: Operation,
3025
target: Union[Model, ModelBase],
3126
status: Status = Status.SUCCESS,
32-
get_time: Callable[[], datetime] = _now,
3327
ip_address: str = "",
3428
additional_information: str = "",
3529
service=None,
@@ -43,7 +37,6 @@ def log(
4337
an operation(e.g. READ or UPDATE), the target of the operation
4438
(a Django model instance), status (e.g. SUCCESS), and a timestamp.
4539
"""
46-
current_time = get_time()
4740
user_id_field_name = getattr(actor, "audit_log_id_field", "pk")
4841
user_id = str(getattr(actor, user_id_field_name, None) or "")
4942

@@ -70,24 +63,20 @@ def log(
7063
"API-Key" if hasattr(actor, "service_api_key") else "Token"
7164
)
7265
target_id = _get_target_id(target)
73-
message = {
74-
"audit_event": {
75-
"origin": settings.AUDIT_LOG_ORIGIN,
76-
"status": status.value,
77-
"date_time_epoch": int(current_time.timestamp() * 1000),
78-
"date_time": _iso8601_date(current_time),
79-
"actor": actor_data,
80-
"operation": operation.value,
81-
"additional_information": additional_information,
82-
"target": {
83-
"type": _get_target_type(target),
84-
"id": target_id,
85-
"lookup_field": lookup_field if target_id else "",
86-
"endpoint": view_name,
87-
},
66+
67+
ResilientLogSource.create_structured(
68+
level=logging.NOTSET,
69+
message=status.value,
70+
actor=actor_data,
71+
operation=operation.value,
72+
target={
73+
"type": _get_target_type(target),
74+
"id": target_id,
75+
"lookup_field": lookup_field if target_id else "",
76+
"endpoint": view_name,
8877
},
89-
}
90-
AuditLogEntry.objects.create(message=message)
78+
extra={"status": status, "additional_information": additional_information},
79+
)
9180

9281

9382
def _get_target_id(target: Union[Model, ModelBase]) -> str:

0 commit comments

Comments
 (0)