Coverage for chalicelib/core/errors/errors_ch.py: 43%
51 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
1import json
2import logging
4from chalicelib.core.sourcemaps import sourcemaps
5from chalicelib.utils import ch_client, pg_client
6from chalicelib.utils import helper
7from chalicelib.utils.TimeUTC import TimeUTC
9logger = logging.getLogger(__name__)
12def get(error_id, project_id) -> dict | None:
13 with ch_client.ClickHouseClient() as cur:
14 # @formatter:off
15 query = """SELECT error_id,
16 project_id,
17 `$properties`.source AS source,
18 'ERROR' AS name,
19 `$properties`.message AS message,
20 `$properties`.payload AS payload,
21 stacktrace,
22 stacktrace_parsed_at
23 FROM product_analytics.events
24 LEFT JOIN (SELECT error_id, stacktrace, stacktrace_parsed_at
25 FROM experimental.parsed_errors
26 WHERE project_id = toUInt16(%(project_id)s)
27 AND error_id = %(error_id)s
28 AND NOT is_deleted
29 ORDER BY stacktrace_parsed_at DESC
30 LIMIT 1) AS parsed USING (error_id)
31 WHERE "$event_name" = 'ERROR'
32 AND error_id = %(error_id)s
33 AND events.project_id = toUInt16(%(project_id)s)
34 LIMIT 1;"""
35 # @formatter:on
36 rows = cur.execute(query, {"error_id": error_id, "project_id": project_id})
37 if len(rows) == 0: 37 ↛ 39line 37 didn't jump to line 39
38 return None
39 return helper.dict_to_camel_case(rows[0])
42def __save_stacktrace(project_id, error_id, data):
43 with ch_client.ClickHouseClient() as cur:
44 query = f"""INSERT INTO experimental.parsed_errors(project_id, error_id, stacktrace)
45 VALUES (%(project_id)s,%(error_id)s,%(data)s);"""
46 params = {"project_id": project_id, "error_id": error_id, "data": json.dumps(data)}
47 cur.execute(query=query, parameters=params)
50def get_trace(project_id, error_id):
51 error = get(error_id=error_id, project_id=project_id)
52 if error is None: 52 ↛ 54line 52 didn't jump to line 54 because the condition on line 52 was always true
53 return {"errors": ["error not found"]}
54 if error.get("source", "") != "js_exception":
55 return {"errors": ["this source of errors doesn't have a sourcemap"]}
56 if error.get("payload") is None or error.get("payload") == "":
57 return {"errors": ["null payload"]}
58 if error.get("stacktrace") is not None and error.get("stacktrace") != "":
59 return {"sourcemapUploaded": True,
60 "trace": json.loads(error.get("stacktrace")),
61 "preparsed": True}
63 payload = json.loads(error["payload"]) if isinstance(error["payload"], str) else error["payload"]
64 trace, all_exists = sourcemaps.get_traces_group(project_id=project_id, payload=payload)
65 if all_exists:
66 __save_stacktrace(project_id=project_id, error_id=error_id, data=trace)
67 return {"sourcemapUploaded": all_exists,
68 "trace": trace,
69 "preparsed": False}
72# TODO: Delete this if UI is not calling the corresponding endpoint
73def get_sessions(start_date, end_date, project_id, user_id, error_id):
74 extra_constraints = ["s.project_id = %(project_id)s",
75 "s.start_ts >= %(startDate)s",
76 "s.start_ts <= %(endDate)s",
77 "e.error_id = %(error_id)s"]
78 if start_date is None: 78 ↛ 79line 78 didn't jump to line 79 because the condition on line 78 was never true
79 start_date = TimeUTC.now(-7)
80 if end_date is None: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true
81 end_date = TimeUTC.now()
83 params = {
84 "startDate": start_date,
85 "endDate": end_date,
86 "project_id": project_id,
87 "userId": user_id,
88 "error_id": error_id}
89 with pg_client.PostgresClient() as cur:
90 query = cur.mogrify(
91 f"""SELECT s.project_id,
92 s.session_id::text AS session_id,
93 s.user_uuid,
94 s.user_id,
95 s.user_agent,
96 s.user_os,
97 s.user_browser,
98 s.user_device,
99 s.user_country,
100 s.start_ts,
101 s.duration,
102 s.events_count,
103 s.pages_count,
104 s.errors_count,
105 s.issue_types,
106 COALESCE((SELECT TRUE
107 FROM public.user_favorite_sessions AS fs
108 WHERE s.session_id = fs.session_id
109 AND fs.user_id = %(userId)s LIMIT 1), FALSE) AS favorite,
110 COALESCE((SELECT TRUE
111 FROM public.user_viewed_sessions AS fs
112 WHERE s.session_id = fs.session_id
113 AND fs.user_id = %(userId)s LIMIT 1), FALSE) AS viewed
114 FROM public.sessions AS s INNER JOIN events.errors AS e USING (session_id)
115 WHERE {" AND ".join(extra_constraints)}
116 ORDER BY s.start_ts DESC;""",
117 params)
118 cur.execute(query=query)
119 sessions_list = []
120 total = cur.rowcount
121 row = cur.fetchone()
122 while row is not None and len(sessions_list) < 100:
123 sessions_list.append(row)
124 row = cur.fetchone()
126 return {
127 'total': total,
128 'sessions': helper.list_to_camel_case(sessions_list)
129 }