def get_classroom(url_rid, url_semester): """教室查询""" # decrypt identifier in URL try: _, room_id = decrypt(url_rid, resource_type='room') except ValueError: return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER) # RPC to get classroom timetable with elasticapm.capture_span('rpc_get_classroom_timetable'): try: room = APIServer.get_classroom_timetable(url_semester, room_id) except Exception as e: return handle_exception_with_error_page(e) with elasticapm.capture_span('process_rpc_result'): cards = defaultdict(list) for card in room.cards: day, time = lesson_string_to_tuple(card.lesson) cards[(day, time)].append(card) empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(cards) available_semesters = semester_calculate(url_semester, room.semesters) return render_template('query/room.html', room=room, cards=cards, empty_sat=empty_sat, empty_sun=empty_sun, empty_6=empty_6, empty_5=empty_5, available_semesters=available_semesters, current_semester=url_semester)
def test_transaction_sample_rate_dynamic(elasticapm_client, not_so_random): elasticapm_client.config.update(version="1", transaction_sample_rate=0.4) for i in range(10): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("xyz"): pass elasticapm_client.end_transaction("test") transactions = elasticapm_client.events[TRANSACTION] spans_per_transaction = defaultdict(list) for span in elasticapm_client.events[SPAN]: spans_per_transaction[span["transaction_id"]].append(span) # seed is fixed by not_so_random fixture assert len([t for t in transactions if t["sampled"]]) == 3 for transaction in transactions: assert transaction[ "sampled"] or not transaction["id"] in spans_per_transaction assert transaction["sampled"] or not "context" in transaction elasticapm_client.config.update(version="1", transaction_sample_rate=1.0) for i in range(5): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("xyz"): pass elasticapm_client.end_transaction("test") transactions = elasticapm_client.events[TRANSACTION] # seed is fixed by not_so_random fixture assert len([t for t in transactions if t["sampled"]]) == 8
def test_transaction_span_frames_min_duration_dynamic(elasticapm_client): elasticapm_client.config.update(version="1", span_frames_min_duration=20) elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("noframes", duration=0.001): pass with elasticapm.capture_span("frames", duration=0.04): pass elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 2 assert spans[0]["name"] == "noframes" assert "stacktrace" not in spans[0] assert spans[1]["name"] == "frames" assert spans[1]["stacktrace"] is not None elasticapm_client.config.update(version="1", span_frames_min_duration=-1) elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("frames"): pass with elasticapm.capture_span("frames", duration=0.04): pass elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 4 assert spans[2]["name"] == "frames" assert spans[2]["stacktrace"] is not None assert spans[3]["name"] == "frames" assert spans[3]["stacktrace"] is not None
def test_transaction_fast_exit_span(elasticapm_client): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span(span_type="x", name="x", leaf=True, duration=2): # not dropped, too long pass with elasticapm.capture_span(span_type="y", name="y", leaf=True, duration=0.1): # dropped pass with elasticapm.capture_span(span_type="z", name="z", leaf=False, duration=0.1): # not dropped, not exit pass elasticapm_client.end_transaction("foo", duration=2.2) transaction = elasticapm_client.events[constants.TRANSACTION][0] spans = elasticapm_client.events[constants.SPAN] breakdown = elasticapm_client._metrics.get_metricset( "elasticapm.metrics.sets.breakdown.BreakdownMetricSet") metrics = list(breakdown.collect()) assert len(spans) == 2 assert transaction["span_count"]["started"] == 3 assert transaction["span_count"]["dropped"] == 1 assert metrics[0]["span"]["type"] == "x" assert metrics[0]["samples"]["span.self_time.sum.us"]["value"] == 2000000 assert metrics[1]["span"]["type"] == "y" assert metrics[1]["samples"]["span.self_time.sum.us"]["value"] == 100000
def test_compressed_spans_not_counted(elasticapm_client): elasticapm_client.begin_transaction("test") with elasticapm.capture_span( "test1", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=2, extra={"destination": { "service": { "resource": "x" } }}, ) as span1: pass with elasticapm.capture_span( "test2", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=3, extra={"destination": { "service": { "resource": "x" } }}, ) as span2: pass elasticapm_client.end_transaction("test") transaction = elasticapm_client.events[TRANSACTION][0] spans = elasticapm_client.events[SPAN] assert len(spans) == transaction["span_count"]["started"] == 1 assert transaction["span_count"]["dropped"] == 0
def bench_transaction_spans(client): client.begin_transaction("test") with capture_span("test1"): with capture_span("test2"): with capture_span("test3"): pass client.end_transaction("test", "OK")
def test_span_compression_disabled(elasticapm_client): transaction = elasticapm_client.begin_transaction("test") with elasticapm.capture_span( "test", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=2, extra={"destination": { "service": { "resource": "x" } }}, ) as span1: assert not span1.is_compression_eligible() with elasticapm.capture_span( "test", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=3, extra={"destination": { "service": { "resource": "x" } }}, ) as span2: assert not span2.is_compression_eligible() elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 2 span = spans[0] assert "composite" not in span
def test_nested_spans(elasticapm_client): elasticapm_client.begin_transaction("request", start=0) with elasticapm.capture_span("test", span_type="template", span_subtype="django", start=5, duration=15): with elasticapm.capture_span("test", span_type="db", span_subtype="mysql", start=10, duration=5): pass with elasticapm.capture_span("test", span_type="db", span_subtype="mysql", start=15, duration=5): pass elasticapm_client.end_transaction("test", "OK", duration=25) breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet") data = list(breakdown.collect()) assert len(data) == 3 asserts = 0 for elem in data: if "span.self_time.sum.us" in elem["samples"]: if elem["span"] == {"type": "app", "subtype": ""}: assert elem["transaction"] == {"name": "test", "type": "request"} assert elem["samples"]["span.self_time.sum.us"]["value"] == 10000000 assert elem["samples"]["span.self_time.count"]["value"] == 1 asserts += 1 elif elem["span"] == {"type": "db", "subtype": "mysql"}: assert elem["samples"]["span.self_time.count"]["value"] == 2 assert elem["samples"]["span.self_time.sum.us"]["value"] == 10000000 assert elem["transaction"] == {"name": "test", "type": "request"} asserts += 1 elif elem["span"] == {"type": "template", "subtype": "django"}: assert elem["samples"]["span.self_time.count"]["value"] == 1 assert elem["samples"]["span.self_time.sum.us"]["value"] == 5000000 assert elem["transaction"] == {"name": "test", "type": "request"} asserts += 1 assert asserts == 3
def test_nested_spans(elasticapm_client): transaction = elasticapm_client.begin_transaction("request") time.sleep(0.005) with elasticapm.capture_span("test", span_type="template", span_subtype="django"): time.sleep(0.005) with elasticapm.capture_span("test", span_type="db", span_subtype="mysql"): time.sleep(0.005) with elasticapm.capture_span("test", span_type="db", span_subtype="mysql"): time.sleep(0.005) time.sleep(0.005) elasticapm_client.end_transaction("test", "OK") breakdown = elasticapm_client._metrics.get_metricset( "elasticapm.metrics.sets.breakdown.BreakdownMetricSet") data = list(breakdown.collect()) assert len(data) == 4 asserts = 0 for elem in data: if "transaction.breakdown.count" in elem["samples"]: assert elem["samples"]["transaction.breakdown.count"]["value"] == 1 assert elem["transaction"] == {"name": "test", "type": "request"} asserts += 1 elif "span.self_time.sum.us" in elem["samples"]: if elem["span"] == {"type": "app", "subtype": ""}: assert elem["transaction"] == { "name": "test", "type": "request" } assert 10000 < elem["samples"]["span.self_time.sum.us"]["value"] assert elem["samples"]["span.self_time.count"]["value"] == 1 asserts += 1 elif elem["span"] == {"type": "db", "subtype": "mysql"}: assert elem["samples"]["span.self_time.count"]["value"] == 2 assert 10000 < elem["samples"]["span.self_time.sum.us"]["value"] assert elem["transaction"] == { "name": "test", "type": "request" } asserts += 1 elif elem["span"] == {"type": "template", "subtype": "django"}: assert elem["samples"]["span.self_time.count"]["value"] == 1 assert 5000 < elem["samples"]["span.self_time.sum.us"]["value"] assert elem["transaction"] == { "name": "test", "type": "request" } asserts += 1 assert asserts == 4 transaction_metrics = elasticapm_client._metrics.get_metricset( "elasticapm.metrics.sets.transactions.TransactionsMetricSet") transaction_data = list(transaction_metrics.collect()) assert len(transaction_data) == 1 assert 25000 < transaction_data[0]["samples"][ "transaction.duration.sum.us"]["value"]
def ics_download(calendar_token): """ iCalendar ics file download 因为课表会更新,所以 ics 文件只能在这里动态生成,不能在日历订阅页面就生成 """ from flask import send_from_directory, current_app from everyclass.server.db.dao import CalendarTokenDAO from everyclass.server.db.model import Semester from everyclass.server.calendar import ics_generator from everyclass.server.utils.rpc import HttpRpc from everyclass.server.utils import teacher_list_fix from everyclass.server.utils import teacher_list_to_str from everyclass.server.utils import lesson_string_to_dict result = CalendarTokenDAO.find_calendar_token(token=calendar_token) if not result: return 'invalid calendar token', 404 with elasticapm.capture_span('rpc_find_people'): rpc_result = HttpRpc.call_with_error_page('{}/v1/{}/{}/{}'.format( current_app.config['API_SERVER_BASE_URL'], result['type'], result['sid'] if result['type'] == 'student' else result['tid'], result['semester']), params={ 'week_string': 'true' }, retry=True) if isinstance(rpc_result, str): return rpc_result api_response = rpc_result with elasticapm.capture_span('process_rpc_result'): semester = Semester(result['semester']) courses = dict() for each_class in api_response['course']: day, time = lesson_string_to_dict(each_class['lesson']) if (day, time) not in courses: courses[(day, time)] = list() courses[(day, time)].append( dict(name=each_class['name'], teacher=teacher_list_to_str( teacher_list_fix(each_class['teacher'])), week=each_class['week'], week_string=each_class['week_string'], classroom=each_class['room'], classroom_id=each_class['rid'], cid=each_class['cid'])) ics_generator.generate(name=api_response['name'], courses=courses, semester=semester, ics_token=calendar_token) return send_from_directory("../../calendar_files", calendar_token + ".ics", as_attachment=True, mimetype='text/calendar')
def ListRecommendations(self, request, context): # manually populate service map # this can be removed once 7.8 is out and the python agent adds this by itself product_catalog_destination_info = { "address": os.environ.get('PRODUCT_CATALOG_SERVICE_ADDR', ''), "port": int(os.environ.get('PORT', 8080)), "service": { "name": "grpc", "resource": os.environ.get('PRODUCT_CATALOG_SERVICE_ADDR', ''), "type": "external" }, } trace_parent = self.extract_trace_parent(context) transaction = client.begin_transaction('request', trace_parent=trace_parent) request_dict = MessageToDict(request) elasticapm.label(**{'request': request_dict}) max_responses = 5 # fetch list of products from product catalog stub list_product_req = demo_pb2.Empty() with elasticapm.capture_span( '/hipstershop.ProductCatalogService/ListProducts', labels=MessageToDict(list_product_req), extra={"destination": product_catalog_destination_info}) as span: trace_parent = transaction.trace_parent.copy_from( span_id=span.id, trace_options=TracingOptions(recorded=True)) cat_response, call = product_catalog_stub.ListProducts.with_call( list_product_req, metadata=[(constants.TRACEPARENT_HEADER_NAME, trace_parent.to_string())]) with elasticapm.capture_span('CalculateRecommendations', span_subtype='grpc', span_action='calculate') as span: product_ids = [x.id for x in cat_response.products] filtered_products = list( set(product_ids) - set(request.product_ids)) num_products = len(filtered_products) num_return = min(max_responses, num_products) # sample list of indicies to return indices = random.sample(range(num_products), num_return) # fetch product ids from indices prod_list = [filtered_products[i] for i in indices] logger.info( '[Recv ListRecommendations] product_ids={}'.format(prod_list), extra=get_extra_logging_payload()) # build and return response response = demo_pb2.ListRecommendationsResponse() response.product_ids.extend(prod_list) elasticapm.label(**{'response': MessageToDict(response)}) elasticapm.set_custom_context({ 'request': request_dict, 'response': MessageToDict(response) }) client.end_transaction( '/hipstershop.RecommendationService/ListRecommendations', 'success') return response
def test_transaction_max_span_nested(elasticapm_client): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("1"): with elasticapm.capture_span("2"): with elasticapm.capture_span("3"): with elasticapm.capture_span("4"): with elasticapm.capture_span("5"): pass with elasticapm.capture_span("6"): pass with elasticapm.capture_span("7"): pass with elasticapm.capture_span("8"): pass with elasticapm.capture_span("9"): pass transaction_obj = elasticapm_client.end_transaction("test") transaction = elasticapm_client.events[TRANSACTION][0] spans = elasticapm_client.events[SPAN] assert transaction_obj.dropped_spans == 6 assert len(spans) == 3 for span in spans: assert span["name"] in ("1", "2", "3") assert transaction["span_count"] == {"dropped": 6, "started": 3}
def test_transaction_max_span_nested(should_collect, elasticapm_client): should_collect.return_value = False elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("1"): with elasticapm.capture_span("2"): with elasticapm.capture_span("3"): with elasticapm.capture_span("4"): with elasticapm.capture_span("5"): pass with elasticapm.capture_span("6"): pass with elasticapm.capture_span("7"): pass with elasticapm.capture_span("8"): pass with elasticapm.capture_span("9"): pass transaction_obj = elasticapm_client.end_transaction("test") transaction = elasticapm_client.transaction_store.get_all()[0] assert transaction_obj.dropped_spans == 6 assert len(transaction["spans"]) == 3 for span in transaction["spans"]: assert span["name"] in ("1", "2", "3") assert transaction["span_count"] == {"dropped": {"total": 6}}
def test_transaction_max_span_nested(should_collect, elasticapm_client): should_collect.return_value = False elasticapm_client.begin_transaction('test_type') with elasticapm.capture_span('1'): with elasticapm.capture_span('2'): with elasticapm.capture_span('3'): with elasticapm.capture_span('4'): with elasticapm.capture_span('5'): pass with elasticapm.capture_span('6'): pass with elasticapm.capture_span('7'): pass with elasticapm.capture_span('8'): pass with elasticapm.capture_span('9'): pass transaction_obj = elasticapm_client.end_transaction('test') transaction = elasticapm_client.instrumentation_store.get_all()[0] assert transaction_obj.dropped_spans == 6 assert len(transaction['spans']) == 3 for span in transaction['spans']: assert span['name'] in ('1', '2', '3') assert transaction['span_count'] == {'dropped': {'total': 6}}
def get_classroom(url_rid, url_semester): """教室查询""" from everyclass.server.utils import lesson_string_to_dict from everyclass.server.utils import teacher_list_fix from everyclass.server.utils import semester_calculate from .utils.rpc import HttpRpc with elasticapm.capture_span('rpc_query_room'): rpc_result = HttpRpc.call_with_error_page('{}/v1/room/{}/{}'.format( app.config['API_SERVER_BASE_URL'], url_rid, url_semester), params={ 'week_string': 'true', 'other_semester': 'true' }, retry=True) if isinstance(rpc_result, str): return rpc_result api_response = rpc_result if 'name' not in api_response: logger.info("Hit classroom 'name' KeyError temporary fix") flash("教务数据异常,暂时无法查询本教室。其他教室不受影响。") return redirect(url_for("main.main")) with elasticapm.capture_span('process_rpc_result'): courses = dict() for each_class in api_response['course']: day, time = lesson_string_to_dict(each_class['lesson']) if (day, time) not in courses: courses[(day, time)] = list() courses[(day, time)].append( dict(name=each_class['name'], week=each_class['week_string'], teacher=teacher_list_fix(each_class['teacher']), location=each_class['room'], cid=each_class['cid'])) empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(courses) available_semesters = semester_calculate( url_semester, sorted(api_response['semester_list'])) return render_template('query/room.html', name=api_response['name'], campus=api_response['campus'], building=api_response['building'], rid=url_rid, classes=courses, empty_sat=empty_sat, empty_sun=empty_sun, empty_6=empty_6, empty_5=empty_5, available_semesters=available_semesters, current_semester=url_semester)
def test_exact_match_after_same_kind(elasticapm_client): # if a span that is an exact match is attempted to be compressed with a same_kind composite, it stays same_kind transaction = elasticapm_client.begin_transaction("test") with elasticapm.capture_span( "test1", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=2, extra={"destination": { "service": { "resource": "x" } }}, ) as span1: assert span1.is_compression_eligible() with elasticapm.capture_span( "test2", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=3, extra={"destination": { "service": { "resource": "x" } }}, ) as span2: assert span2.is_compression_eligible() assert not span1.is_exact_match(span2) assert span1.is_same_kind(span2) with elasticapm.capture_span( "test1", span_type="a", span_subtype="b", span_action="c", leaf=True, duration=2, extra={"destination": { "service": { "resource": "x" } }}, ) as span3: assert span3.is_compression_eligible() elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 1 span = spans[0] assert span["composite"]["compression_strategy"] == "same_kind" assert span["composite"]["count"] == 3
def android_client_get_ics(resource_type, identifier, semester): """ android client get a student or teacher's ics file If the student does not have privacy mode, anyone can use student number to subscribe his calendar. If the privacy mode is on and there is no HTTP basic authentication, return a 401(unauthorized) status code and the Android client ask user for password to try again. """ from flask import current_app as app, redirect, url_for, request from everyclass.server.utils.rpc import HttpRpc from everyclass.server.db.dao import PrivacySettingsDAO, CalendarTokenDAO, UserDAO if resource_type not in ('student', 'teacher'): return "Unknown resource type", 400 with elasticapm.capture_span('rpc_search'): rpc_result = HttpRpc.call_with_handle_message('{}/v1/{}/{}/{}'.format( app.config['API_SERVER_BASE_URL'], resource_type, identifier, semester)) if isinstance(rpc_result, tuple): return rpc_result api_response = rpc_result if resource_type == 'teacher': cal_token = CalendarTokenDAO.get_or_set_calendar_token( resource_type=resource_type, identifier=rpc_result["sid"], semester=semester) return redirect( url_for('calendar.ics_download', calendar_token=cal_token)) else: # student with elasticapm.capture_span('get_privacy_settings'): privacy_level = PrivacySettingsDAO.get_level(api_response['sid']) # get authorization from HTTP header and verify password if privacy is on if privacy_level != 0: if not request.authorization: return "Unauthorized (privacy on)", 401 username, password = request.authorization if not UserDAO.check_password(username, password): return "Unauthorized (password wrong)", 401 if api_response['sid'] != username: return "Unauthorized (username mismatch)", 401 cal_token = CalendarTokenDAO.get_or_set_calendar_token( resource_type=resource_type, identifier=rpc_result["sid"], semester=semester) return redirect( url_for('calendar.ics_download', calendar_token=cal_token))
def get_student(url_sid: str, url_semester: str): """学生查询""" # decrypt identifier in URL try: _, student_id = decrypt(url_sid, resource_type='student') except ValueError: return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER) # RPC 获得学生课表 with elasticapm.capture_span('rpc_get_student_timetable'): try: student = APIServer.get_student_timetable(student_id, url_semester) except Exception as e: return handle_exception_with_error_page(e) # save sid_orig to session for verifying purpose # must be placed before privacy level check. Otherwise a registered user could be redirected to register page. session[SESSION_LAST_VIEWED_STUDENT] = StudentSession( sid_orig=student.student_id, sid=student.student_id_encoded, name=student.name) # 权限检查,如果没有权限则返回 has_permission, return_val = check_permission(student) if not has_permission: return return_val with elasticapm.capture_span('process_rpc_result'): cards: Dict[Tuple[int, int], List[Dict[str, str]]] = dict() for card in student.cards: day, time = lesson_string_to_tuple(card.lesson) if (day, time) not in cards: cards[(day, time)] = list() cards[(day, time)].append(card) empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(cards) available_semesters = semester_calculate(url_semester, sorted(student.semesters)) # 增加访客记录 Redis.add_visitor_count(student.student_id, session.get(SESSION_CURRENT_USER, None)) return render_template('query/student.html', student=student, cards=cards, empty_sat=empty_sat, empty_sun=empty_sun, empty_6=empty_6, empty_5=empty_5, available_semesters=available_semesters, current_semester=url_semester)
def legacy_get_ics(student_id, semester_str): """ legacy iCalendar endpoint query the student first, if the student is not privacy protected, redirect to new ics. else return 401. this route is bad. however, many users have already been using it. breaking experience is bad. so we have to keep the route here for now. and (maybe) remove it in the future. """ from flask import current_app as app, abort, redirect, url_for from everyclass.server.db.dao import PrivacySettingsDAO, CalendarTokenDAO from everyclass.server.utils.rpc import HttpRpc from everyclass.server.db.model import Semester # fix parameters place = student_id.find('-') semester_str = student_id[place + 1:len(student_id)] + '-' + semester_str student_id = student_id[:place] semester = Semester(semester_str) with elasticapm.capture_span('rpc_search'): rpc_result = HttpRpc.call_with_error_page('{}/v1/search/{}'.format( app.config['API_SERVER_BASE_URL'], student_id), retry=True) if isinstance(rpc_result, str): return rpc_result api_response = rpc_result if len(api_response['student']) != 1: # bad request return abort(400) if semester.to_str() not in api_response['student'][0]['semester']: return abort(400) with elasticapm.capture_span('get_privacy_settings'): privacy_settings = PrivacySettingsDAO.get_level( api_response['student'][0]['sid_orig']) if privacy_settings != 0: # force user to get a calendar token when the user is privacy-protected but accessed through legacy interface return "Visit {} to get your calendar".format( url_for("main.main")), 401 else: token = CalendarTokenDAO.get_or_set_calendar_token( resource_type="student", identifier=api_response['student'][0]['sid_orig'], semester=semester.to_str()) return redirect(url_for('calendar.ics_download', calendar_token=token))
def test_outcome_by_span_exception(elasticapm_client): elasticapm_client.begin_transaction("test") try: with elasticapm.capture_span("fail", "test_type"): assert False except AssertionError: pass with elasticapm.capture_span("success", "test_type"): pass elasticapm_client.end_transaction("test") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) assert spans[0]["name"] == "fail" and spans[0]["outcome"] == "failure" assert spans[1]["name"] == "success" and spans[1]["outcome"] == "success"
def get_teacher(url_tid, url_semester): """老师查询""" from everyclass.server.utils import lesson_string_to_dict from everyclass.server.utils import semester_calculate from .utils.rpc import HttpRpc with elasticapm.capture_span('rpc_query_student'): rpc_result = HttpRpc.call_with_error_page('{}/v1/teacher/{}/{}'.format( app.config['API_SERVER_BASE_URL'], url_tid, url_semester), params={ 'week_string': 'true', 'other_semester': 'true' }, retry=True) if isinstance(rpc_result, str): return rpc_result api_response = rpc_result with elasticapm.capture_span('process_rpc_result'): courses = dict() for each_class in api_response['course']: day, time = lesson_string_to_dict(each_class['lesson']) if (day, time) not in courses: courses[(day, time)] = list() courses[(day, time)].append( dict(name=each_class['name'], week=each_class['week_string'], classroom=each_class['room'], classroom_id=each_class['rid'], cid=each_class['cid'])) empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(courses) available_semesters = semester_calculate( url_semester, sorted(api_response['semester_list'])) return render_template('query/teacher.html', name=api_response['name'], falculty=api_response['unit'], title=api_response['title'], tid=url_tid, classes=courses, empty_sat=empty_sat, empty_sun=empty_sun, empty_6=empty_6, empty_5=empty_5, available_semesters=available_semesters, current_semester=url_semester)
def test_transaction_span_frames_min_duration(elasticapm_client): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("noframes", duration=0.001): pass with elasticapm.capture_span("frames", duration=0.04): pass elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 2 assert spans[0]["name"] == "noframes" assert "stacktrace" not in spans[0] assert spans[1]["name"] == "frames" assert spans[1]["stacktrace"] is not None
def test_transaction_keyword_truncation(sending_elasticapm_client): too_long = 'x' * (KEYWORD_MAX_LENGTH + 1) expected = encoding.keyword_field(too_long) assert too_long != expected assert len(expected) == KEYWORD_MAX_LENGTH assert expected[-1] != 'x' sending_elasticapm_client.begin_transaction(too_long) elasticapm.tag(val=too_long) elasticapm.set_user_context(username=too_long, email=too_long, user_id=too_long) with elasticapm.capture_span(name=too_long, span_type=too_long): pass sending_elasticapm_client.end_transaction(too_long, too_long) sending_elasticapm_client.close() assert sending_elasticapm_client.httpserver.responses[0]['code'] == 202 transaction = sending_elasticapm_client.httpserver.payloads[0]['transactions'][0] span = transaction['spans'][0] assert transaction['name'] == expected assert transaction['type'] == expected assert transaction['result'] == expected assert transaction['context']['user']['id'] == expected assert transaction['context']['user']['username'] == expected assert transaction['context']['user']['email'] == expected assert transaction['context']['tags']['val'] == expected assert span['type'] == expected assert span['name'] == expected
def get_card(url_cid: str, url_semester: str): """课程查询""" # decrypt identifier in URL try: _, card_id = decrypt(url_cid, resource_type='klass') except ValueError: return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER) # RPC to get card with elasticapm.capture_span('rpc_get_card'): try: card = APIServer.get_card(url_semester, card_id) except Exception as e: return handle_exception_with_error_page(e) day, time = lesson_string_to_tuple(card.lesson) # 给“文化素质类”等加上“课”后缀 if card.type and card.type[-1] != '课': card.type = card.type + '课' cotc_id = COTeachingClass.get_id_by_card(card) course_review_doc = CourseReview.get_review(cotc_id) return render_template( 'query/card.html', card=card, card_day=get_day_chinese(day), card_time=get_time_chinese(time), show_union_class=not card.union_name.isdigit(), # 合班名称为数字时不展示合班名称 cotc_id=cotc_id, cotc_rating=course_review_doc["avg_rate"], current_semester=url_semester)
def test_collect_source_transactions(should_collect, elasticapm_client): should_collect.return_value = False library_frame_context = elasticapm_client.config.source_lines_span_library_frames in_app_frame_context = elasticapm_client.config.source_lines_span_app_frames elasticapm_client.begin_transaction('test') with elasticapm.capture_span('foo'): pass elasticapm_client.end_transaction('test', 'ok') transaction = elasticapm_client.instrumentation_store.get_all()[0] in_app_frame = transaction['spans'][0]['stacktrace'][0] library_frame = transaction['spans'][0]['stacktrace'][1] assert not in_app_frame['library_frame'] assert library_frame['library_frame'] if library_frame_context: assert 'context_line' in library_frame, library_frame_context assert 'pre_context' in library_frame, library_frame_context assert 'post_context' in library_frame, library_frame_context lines = len([library_frame['context_line']] + library_frame['pre_context'] + library_frame['post_context']) assert lines == library_frame_context, library_frame_context else: assert 'context_line' not in library_frame, library_frame_context assert 'pre_context' not in library_frame, library_frame_context assert 'post_context' not in library_frame, library_frame_context if in_app_frame_context: assert 'context_line' in in_app_frame, in_app_frame_context assert 'pre_context' in in_app_frame, in_app_frame_context assert 'post_context' in in_app_frame, in_app_frame_context lines = len([in_app_frame['context_line']] + in_app_frame['pre_context'] + in_app_frame['post_context']) assert lines == in_app_frame_context, (in_app_frame_context, in_app_frame['lineno']) else: assert 'context_line' not in in_app_frame, in_app_frame_context assert 'pre_context' not in in_app_frame, in_app_frame_context assert 'post_context' not in in_app_frame, in_app_frame_context
def test_skip_ignored_frames(elasticapm_client): elasticapm_client.begin_transaction('test') with elasticapm.capture_span('test'): pass transaction = elasticapm_client.end_transaction('test', 'test') for frame in transaction.spans[0].frames: assert not frame['module'].startswith('elasticapm')
def test_collect_source_transactions(elasticapm_client): library_frame_context = elasticapm_client.config.source_lines_span_library_frames in_app_frame_context = elasticapm_client.config.source_lines_span_app_frames elasticapm_client.begin_transaction("test") with elasticapm.capture_span("foo"): pass elasticapm_client.end_transaction("test", "ok") span = elasticapm_client.events[SPAN][0] in_app_frame = span["stacktrace"][0] library_frame = span["stacktrace"][1] assert not in_app_frame["library_frame"] assert library_frame["library_frame"] if library_frame_context: assert "context_line" in library_frame, library_frame_context assert "pre_context" in library_frame, library_frame_context assert "post_context" in library_frame, library_frame_context lines = len([library_frame["context_line"]] + library_frame["pre_context"] + library_frame["post_context"]) assert lines == library_frame_context, library_frame_context else: assert "context_line" not in library_frame, library_frame_context assert "pre_context" not in library_frame, library_frame_context assert "post_context" not in library_frame, library_frame_context if in_app_frame_context: assert "context_line" in in_app_frame, in_app_frame_context assert "pre_context" in in_app_frame, in_app_frame_context assert "post_context" in in_app_frame, in_app_frame_context lines = len([in_app_frame["context_line"]] + in_app_frame["pre_context"] + in_app_frame["post_context"]) assert lines == in_app_frame_context, (in_app_frame_context, in_app_frame["lineno"]) else: assert "context_line" not in in_app_frame, in_app_frame_context assert "pre_context" not in in_app_frame, in_app_frame_context assert "post_context" not in in_app_frame, in_app_frame_context
def call(self, module, method, wrapped, instance, args, kwargs): args_len = len(args) http_method = args[0] if args_len else kwargs.get("method") http_path = args[1] if args_len > 1 else kwargs.get("url") params = args[2] if args_len > 2 else kwargs.get("params") body = params.pop(BODY_REF_NAME, None) if params else None api_method = params.pop(API_METHOD_KEY_NAME, None) if params else None signature = "ES %s %s" % (http_method, http_path) context = {"db": {"type": "elasticsearch"}} if api_method in self.query_methods: query = [] # using both q AND body is allowed in some API endpoints / ES versions, # but not in others. We simply capture both if they are there so the # user can see it. if params and "q" in params: # 'q' is already encoded to a byte string at this point # we assume utf8, which is the default query.append("q=" + params["q"].decode("utf-8", errors="replace")) if isinstance(body, dict) and "query" in body: query.append(json.dumps(body["query"], default=compat.text_type)) context["db"]["statement"] = "\n\n".join(query) elif api_method == "Elasticsearch.update": if isinstance(body, dict) and "script" in body: # only get the `script` field from the body context["db"]["statement"] = json.dumps({"script": body["script"]}) # TODO: add instance.base_url to context once we agreed on a format with elasticapm.capture_span(signature, "db.elasticsearch", extra=context, skip_frames=2, leaf=True): return wrapped(*args, **kwargs)
def test_transaction_keyword_truncation(elasticapm_client): too_long = "x" * (KEYWORD_MAX_LENGTH + 1) expected = encoding.keyword_field(too_long) assert too_long != expected assert len(expected) == KEYWORD_MAX_LENGTH assert expected[-1] != "x" elasticapm_client.begin_transaction(too_long) elasticapm.tag(val=too_long) elasticapm.set_user_context(username=too_long, email=too_long, user_id=too_long) with elasticapm.capture_span(name=too_long, span_type=too_long): pass elasticapm_client.end_transaction(too_long, too_long) elasticapm_client.close() span = elasticapm_client.events["span"][0] transaction = elasticapm_client.events["transaction"][0] assert transaction["name"] == expected assert transaction["type"] == expected assert transaction["result"] == expected assert transaction["context"]["user"]["id"] == expected assert transaction["context"]["user"]["username"] == expected assert transaction["context"]["user"]["email"] == expected assert transaction["context"]["tags"]["val"] == expected assert span["type"] == expected assert span["name"] == expected
def test_transaction_span_frames_min_duration_no_limit(elasticapm_client): elasticapm_client.begin_transaction("test_type") with elasticapm.capture_span("frames"): pass with elasticapm.capture_span("frames"): time.sleep(0.040) elasticapm_client.end_transaction("test") spans = elasticapm_client.events[SPAN] assert len(spans) == 2 assert spans[0]["name"] == "frames" assert spans[0]["stacktrace"] is not None assert spans[1]["name"] == "frames" assert spans[1]["stacktrace"] is not None