Skip to content

Commit fca2cab

Browse files
author
Tycho Hob
committed
fix: Fix Vector formatter, add tests
1 parent 10c3be6 commit fca2cab

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

xapi_db_load/backends/vector.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ def __init__(self, config: dict, logger: Logger, event_generator: EventGenerator
6060
super().__init__(config, logger, event_generator)
6161

6262
stream_handler = logging.StreamHandler(sys.stdout)
63-
formatter = logging.Formatter("[%(name)s] %(message)s")
63+
# This formatter is different from what the LMS uses, but is the smallest possible
64+
# format that passes Vector's regex
65+
formatter = logging.Formatter(" [{name}] [] {message}", style="{")
6466
stream_handler.setFormatter(formatter)
6567
self.xapi_logger = getLogger("xapi_tracking")
6668
self.xapi_logger.setLevel(logging.INFO)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Test configuration for Ralph / ClickHouse
2+
# #########################################
3+
backend: vector
4+
db_host: localhost
5+
db_port: 8123
6+
db_name: xapi
7+
db_username: ch_admin
8+
db_password: foo
9+
10+
# Run options
11+
log_dir: logs
12+
num_xapi_batches: 3
13+
batch_size: 100
14+
15+
# This number is used for each QueueBackend that use workers, so the number of threads if
16+
# multiplicative. Generally this performs best less than 10, as more threads will cost more
17+
# in context switching than they save.
18+
num_workers: 4
19+
20+
# Overall start and end date for the entire run
21+
start_date: 2014-01-01
22+
end_date: 2023-11-27
23+
24+
# All courses will be this long, and be fit into the start / end dates
25+
# This must be less than end_date - start_date days.
26+
course_length_days: 120
27+
28+
# The size of the test
29+
num_organizations: 3
30+
num_actors: 10
31+
32+
# This replicates users updating their profiles several times, creating
33+
# more rows
34+
num_actor_profile_changes: 5
35+
36+
# How many of each size course to create. The sum of these is the total number
37+
# of courses created for the test.
38+
num_course_sizes:
39+
small: 1
40+
41+
# How many times each course will be "published", this creates a more realistic
42+
# distribution of course blocks where each course can be published dozens or
43+
# hundreds of times while it is being developed.
44+
num_course_publishes: 10
45+
46+
course_size_makeup:
47+
small:
48+
actors: 5
49+
problems: 20
50+
videos: 10
51+
chapters: 3
52+
sequences: 10
53+
verticals: 20
54+
forum_posts: 20

xapi_db_load/tests/test_backends.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"""
44

55
import gzip
6+
import json
67
import os
8+
import re
79
from contextlib import contextmanager
810
from unittest.mock import AsyncMock, MagicMock, patch
911

@@ -120,6 +122,66 @@ def test_clickhouse_backend(_, tmp_path):
120122
assert "Run duration was" in result.output
121123

122124

125+
@patch(
126+
"xapi_db_load.backends.base_async_backend.clickhouse_connect",
127+
new_callable=AsyncMock,
128+
)
129+
@patch(
130+
"xapi_db_load.backends.vector.getLogger",
131+
new_callable=MagicMock,
132+
)
133+
def test_vector_backend(mock_get_logger, _, tmp_path):
134+
"""
135+
Run a test through the Vector backend, currently this just checks that the
136+
output indicates success.
137+
"""
138+
test_path = "xapi_db_load/tests/fixtures/small_vector_config.yaml"
139+
140+
runner = CliRunner()
141+
142+
with override_config(test_path, tmp_path):
143+
result = runner.invoke(
144+
load_db,
145+
f"--config_file {test_path}",
146+
catch_exceptions=False,
147+
)
148+
149+
# This test should create 300 xAPI log statemetns
150+
assert mock_get_logger.return_value.info.call_count == 300
151+
152+
last_logged_statement = mock_get_logger.return_value.info.call_args.args[0]
153+
154+
# We check to make sure Vector's regex will parse what we're sending. We want it to match both
155+
# the LMS and our local logger formatter.
156+
# This is how things are generally formatted in the LMS
157+
test_str_1 = f"2026-02-24 20:26:13,006 INFO 42 [xapi_tracking] [user None] [ip 172.19.0.1] logger.py:41 - {last_logged_statement}"
158+
159+
# This returns our message formatted with the abbreviated version we use for size and speed purposes
160+
formatter = mock_get_logger.return_value.addHandler.call_args.args[0].formatter
161+
test_str_2 = formatter._fmt.format(
162+
name="xapi_tracking", message=last_logged_statement
163+
)
164+
165+
# This is a direct copy and paste from Aspects' Vector common-post.toml
166+
msg_regex = r"^.* \[xapi_tracking\] [^{}]* (?P<tracking_message>\{.*\})$"
167+
168+
# Quick test to make sure that what's being stored is at least parseable
169+
for s in (test_str_1, test_str_2):
170+
try:
171+
statement = re.match(msg_regex, s).groups()[0]
172+
json.loads(statement)
173+
except Exception as e:
174+
print(e)
175+
print("Exception! Regex testing: ")
176+
print(s)
177+
raise
178+
179+
assert "Insert xAPI Events complete." in result.output
180+
assert "Insert Initial Enrollments complete." in result.output
181+
assert "ALL TASKS DONE!" in result.output
182+
assert "Run duration was" in result.output
183+
184+
123185
@patch("xapi_db_load.backends.ralph.requests", new_callable=AsyncMock)
124186
@patch(
125187
"xapi_db_load.backends.base_async_backend.clickhouse_connect",

0 commit comments

Comments
 (0)