async def test_prepare(loop, postgres_url): app = BaseApplication(BaseConfig()) app.add( 'db', Postgres( PostgresConfig(url=postgres_url, log_result=True, log_query=True)), ) await app.start() db: Postgres = app.get('db') # type: ignore async with db.connection() as conn: st = await conn.prepare('SELECT $1::int as a', query_name='db4') row = await st.query_one(10) assert row['a'] == 10 rows = await st.query_all(20) assert len(rows) == 1 assert rows[0]['a'] == 20 st2 = await conn.prepare('SELECT $1::text as a') row = await st2.query_one('30') assert row['a'] == '30' rows = await st2.query_all('30') assert len(rows) == 1 assert rows[0]['a'] == '30'
async def create_lock(url: Optional[str]) -> Lock: app = BaseApplication(BaseConfig()) app.add( 'lock', Lock(LockConfig(url=url)), ) await app.start() lock: Lock = app.get('lock') # type: ignore return lock
async def test_sftp(loop) -> None: app = BaseApplication(BaseConfig()) app.add( "sftp", SftpClient( SftpClientConfig( url="sftp://*****:*****@localhost:2222/upload")), ) await app.start() sftp: SftpClient = app.get("sftp") # type: ignore test_data = "Test data" base_remote_path = await sftp.getcwd() assert base_remote_path == "/upload" remote_path = os.path.join(base_remote_path, str(uuid.uuid4())) await sftp.mkdir(remote_path) await sftp.chdir(remote_path) with TemporaryDirectory() as local_path: local_file_out = os.path.join(local_path, f"{uuid.uuid4()}_out.txt") local_file_ren = os.path.join(local_path, f"{uuid.uuid4()}_ren.txt") local_file_in = os.path.join(local_path, f"{uuid.uuid4()}_in.txt") with open(local_file_out, "w") as f: f.write(test_data) remote_file_out = os.path.join(remote_path, os.path.basename(local_file_out)) remote_file_ren = os.path.join(remote_path, os.path.basename(local_file_ren)) await sftp.put(local_file_out, remote_file_out) assert await sftp.exists(remote_file_out) is True assert os.path.basename(local_file_out) in await sftp.listdir( remote_path) await sftp.get(remote_file_out, local_file_in) with open(local_file_in) as f: assert f.read() == test_data await sftp.rename(remote_file_out, remote_file_ren) assert await sftp.exists(remote_file_ren) is True assert os.path.basename(remote_file_ren) in [ path.filename for path in await sftp.readdir(remote_path) ] await sftp.remove(remote_file_ren) assert await sftp.exists(remote_file_ren) is False assert os.path.basename(remote_file_ren) not in await sftp.listdir( remote_path) await app.stop()
async def s3() -> AsyncGenerator[S3, None]: s3 = S3( S3Config( endpoint_url='http://127.0.0.1:9000', aws_access_key_id='EXAMPLEACCESSKEY', aws_secret_access_key='EXAMPLESECRETKEY', )) app = BaseApplication(BaseConfig()) app.add('s3', s3) await app.start() yield s3 await app.stop()
async def startup() -> [BaseApplication, Oracle]: app = BaseApplication(BaseConfig()) app.add( 'db', Oracle( OracleConfig( user=oracle_user, password=oracle_pwd, dsn=oracle_host, )), ) await app.start() db: Oracle = app.get('db') # type: ignore return app, db
async def test_success(loop): async with SentryServer() as ss: cfg = SentryConfig(dsn=ss.addr) adapter = SentryAdapter(cfg) app = BaseApplication(BaseConfig()) app.logger.add(adapter) lgr = app.logger await lgr.start() with lgr.span_new(name='t1', kind=Span.KIND_SERVER) as span: span.tag('tag', 'abc') span.annotate('k1', 'val1', ts=123456) with pytest.raises(Exception): with span.new_child('t2', Span.KIND_CLIENT): raise Exception('bla bla') await lgr.stop() assert len(ss.errors) == 1 err = ss.errors[0] assert err['level'] == 'error', str(err) assert 'exception' in err, str(err) assert 'event_id' in err, str(err) assert 'timestamp' in err, str(err) assert 'breadcrumbs' in err, str(err) assert 'contexts' in err, str(err) assert 'platform' in err, str(err) assert err['exception']['values'][0]['type'] == 'Exception', str(err) assert err['exception']['values'][0]['value'] == 'bla bla', str(err)
async def test_statement_cache_size(loop, postgres_url): app = BaseApplication(BaseConfig()) app.add( 'db', Postgres( PostgresConfig( url=postgres_url, log_result=True, log_query=True, statement_cache_size=0, )), ) await app.start() db: Postgres = app.get('db') # type: ignore async with db.connection() as conn: assert conn._conn._stmt_cache._max_size == 0 await app.stop()
async def test_pika(loop, rabbitmq_url): messages: List[Tuple[bytes]] = [] class TestPubChg(PikaChannel): name = 'pub' class TestCnsChg(PikaChannel): name = 'sub' async def prepare(self) -> None: await self.exchange_declare('myexchange', durable=False) await self.queue_declare('myqueue', durable=False) await self.queue_bind('myqueue', 'myexchange', '') await self.qos(prefetch_count=1) async def start(self) -> None: await self.consume('myqueue', self.message) async def message( self, body: bytes, deliver: Deliver, proprties: Properties ) -> None: await self.ack(delivery_tag=deliver.delivery_tag) messages.append((body,)) app = BaseApplication(BaseConfig()) app.add( 'mq', Pika( PikaConfig(url=rabbitmq_url), [ lambda: TestPubChg(PikaChannelConfig()), lambda: TestCnsChg(PikaChannelConfig()), ], ), ) await app.start() mq: Pika = app.get('mq') # type: ignore await mq.channel('pub').publish('myexchange', '', 'testmsg') await wait_for(lambda: len(messages) > 0) assert messages == [(b'testmsg',)] await app.stop()
async def test_span_from_hdrs_new_2(): app = BaseApplication(BaseConfig()) lgr = app.logger with lgr.span_from_headers(None) as span: assert span.trace_id is not None assert span.id is not None assert span.parent_id is None assert not span._skip
async def test_zipkin_trace_id_size_settings( loop, use_64bit_trace_id: bool, trace_id_string_length: int ): app = BaseApplication(BaseConfig()) lgr = app.logger cfg = ZipkinConfig(name='123', use_64bit_trace_id=use_64bit_trace_id) lgr.add(ZipkinAdapter(cfg)) with lgr.span_new(name='test_span') as span: assert len(span.trace_id) == trace_id_string_length
async def test_span_from_hdrs_1(): app = BaseApplication(BaseConfig()) lgr = app.logger hdrs = {'X-B3-TraceId': 'f3d33b325b9458f1a18de5f226ea54d8'} with lgr.span_from_headers(hdrs) as span: assert span.trace_id == 'f3d33b325b9458f1a18de5f226ea54d8' assert span.id is not None assert span.parent_id is None assert not span._skip
async def test_span_ctx(): app = BaseApplication(BaseConfig()) lgr = app.logger assert ctx_span_get() is None with lgr.span_new() as span1: assert ctx_span_get() is span1 with span1.new_child() as span2: assert ctx_span_get() is span2 assert ctx_span_get() is span1 assert ctx_span_get() is None
async def test_span_from_hdrs_2(): app = BaseApplication(BaseConfig()) lgr = app.logger hdrs = {'X-B3-TraceId': ''} with lgr.span_from_headers(hdrs) as span: assert span.trace_id is not None assert span.trace_id != '' assert span.id is not None assert span.parent_id is None assert not span._skip
async def test_errors(loop, ): app = BaseApplication(BaseConfig()) lgr = app.logger cfg = ZipkinConfig(name='123') adapter = ZipkinAdapter(cfg) with pytest.raises(AdapterConfigurationError): adapter.handle(lgr.span_new()) with pytest.raises(AdapterConfigurationError): await adapter.stop()
async def test_base(loop, postgres_url): app = BaseApplication(BaseConfig()) app.add( 'db', Postgres( PostgresConfig(url=postgres_url, log_result=True, log_query=True)), ) await app.start() db: Postgres = app.get('db') # type: ignore res = await db.execute('SELECT $1::int as a', 10, query_name='db1') assert 'SELECT 1' == res res = await db.query_one('SELECT $1::int as a', 10, query_name='db2') assert isinstance(res, asyncpg.Record) assert res['a'] == 10 # json res = await db.query_one('SELECT $1::json as a', {'a': 1}) assert isinstance(res, asyncpg.Record) assert res['a'] == {'a': 1} # jsonb res = await db.query_one('SELECT $1::jsonb as a', {'a': 1}) assert isinstance(res, asyncpg.Record) assert res['a'] == {'a': 1} res = await db.query_one('SELECT 1 as a WHERE FALSE') assert res is None res = await db.query_all('SELECT $1::int as a', 10, query_name='db3') assert isinstance(res, list) assert len(res) == 1 assert isinstance(res[0], asyncpg.Record) assert res[0]['a'] == 10 res = await db.query_all('SELECT 1 as a WHERE FALSE') assert isinstance(res, list) assert len(res) == 0 await app.stop()
async def test_xact(loop, postgres_url): app = BaseApplication(BaseConfig()) app.add( 'db', Postgres( PostgresConfig(url=postgres_url, log_result=True, log_query=True)), ) await app.start() db: Postgres = app.get('db') # type: ignore async with db.connection() as conn: await conn.execute('CREATE TEMPORARY TABLE _test_xact(id int)') try: async with conn.xact(): await conn.execute('INSERT INTO _test_xact(id) VALUES (12)') r = await conn.query_one('SELECT COUNT(*) c FROM _test_xact') assert r['c'] == 1 raise Exception('rollback') except Exception as err: assert str(err) == 'rollback', err r = await conn.query_one('SELECT COUNT(*) c FROM _test_xact') assert r['c'] == 0
async def test_span_from_hdrs_skippable(): app = BaseApplication(BaseConfig()) lgr = app.logger hdrs = { 'X-B3-TraceId': 'f3d33b325b9458f1a18de5f226ea54d8', 'X-B3-SpanId': 'ed8753e2153a20df', 'X-B3-Sampled': '0', } with lgr.span_from_headers(hdrs) as span: assert span.trace_id == 'f3d33b325b9458f1a18de5f226ea54d8' assert span.id is not None assert span.parent_id == 'ed8753e2153a20df' assert span._skip
async def test_success(loop, unused_tcp_port): port = unused_tcp_port cfg = PrometheusConfig( port=port, hist_labels={ 'test_span': { 'le': '1.1,Inf', # le mapping to quantiles 'tag1': 'some_tag1', } }, ) adapter = PrometheusAdapter(cfg) app = BaseApplication(BaseConfig()) app.logger.add(adapter) await app.start() with app.logger.span_new(name='test_span') as span: pass with app.logger.span_new(name='test_span') as span2: span2.tag('some_tag1', '123') with app.logger.span_new(name='test_span') as span3: span3.tag('some_tag1', '123') with app.logger.span_new(cls=PgSpan) as span4: span4.set_name4adapter(app.logger.ADAPTER_PROMETHEUS, PgSpan.P8S_NAME_ACQUIRE) async with ClientSession() as sess: resp = await sess.get('http://127.0.0.1:%d/' % port) txt = await resp.text() assert 'test_span_bucket{le="1.1",tag1=""} 1.0' in txt assert 'test_span_bucket{le="+Inf",tag1=""} 1.0' in txt assert 'test_span_count{tag1=""} 1.0' in txt assert 'test_span_bucket{le="1.1",tag1="123"} 2.0' in txt assert 'test_span_bucket{le="+Inf",tag1="123"} 2.0' in txt assert 'test_span_count{tag1="123"} 2.0' in txt assert ('test_span_sum{tag1=""} %s' % span.duration) in txt assert ('test_span_sum{tag1="123"} %s' % (span2.duration + span3.duration)) in txt assert 'db_connection_count{error="",free=""} 1.0' in txt await app.stop()
async def test_trap(): app = BaseApplication(BaseConfig()) lgr = app.logger class ExSpan(Span): pass assert ctx_span_get() is None with app.logger.capture_span(ExSpan) as trap: with pytest.raises(UserWarning): assert trap.span is None with lgr.span_new(name='t1') as span1: with span1.new_child(name='t2', cls=ExSpan): pass assert trap.span is not None assert trap.span.name == 't2'
async def test_span_to_hdrs_non_skippable(): app = BaseApplication(BaseConfig()) lgr = app.logger with lgr.span_new() as span: assert span.to_headers() == { 'X-B3-TraceId': span.trace_id, 'X-B3-SpanId': span.id, 'X-B3-Flags': '0', 'X-B3-Sampled': '1', } with span.new_child() as span2: assert span2.to_headers() == { 'X-B3-TraceId': span2.trace_id, 'X-B3-SpanId': span2.id, 'X-B3-ParentSpanId': span2.parent_id, 'X-B3-Flags': '0', 'X-B3-Sampled': '1', }
async def test_logger_span_callback(): class TestAdapter(AbcAdapter): started = False stopped = False handled: List[Span] = [] async def start(self, logger: 'Logger'): self.started = True def handle(self, span: Span): self.handled.append(span) async def stop(self): self.stopped = True def replace_tags(span: Span) -> None: for tag in span.tags.keys(): span.tag(tag, '***') app = BaseApplication(BaseConfig()) lgr = app.logger lgr.add(TestAdapter()) adapter = lgr.adapters[TestAdapter.__name__] assert isinstance(adapter, TestAdapter) lgr.add_before_handle_cb(replace_tags) await lgr.start() assert adapter.started with lgr.span_new() as span: span.tag('tag', 'abc') await lgr.stop() assert adapter.stopped assert len(adapter.handled) == 1 assert span.start_stamp is not None assert span.finish_stamp is not None assert 'tag' in span.tags assert span.tags['tag'] == '***'
async def test_success(loop, ): async with ZipkinServer() as zs: cfg = ZipkinConfig(name='123', addr=zs.addr) adapter = ZipkinAdapter(cfg) app = BaseApplication(BaseConfig()) app.logger.add(adapter) lgr = app.logger await lgr.start() with lgr.span_new(name='t1', kind=Span.KIND_SERVER) as sp: sp.tag('tag', 'abc') sp.annotate('k1', 'val1', ts=123456) with pytest.raises(Exception): with sp.new_child('t2', Span.KIND_CLIENT): raise Exception() await lgr.stop() assert len(zs.spans) == 2 span: SpanModel = zs.spans[1] span2: SpanModel = zs.spans[0] assert span.name == 't1' assert span.tags == {'tag': 'abc'} assert span.annotations == [ Annotation(value='val1', timestamp=123456000000) ] assert span.duration > 0 assert span.timestamp > 0 assert not span.debug assert span.shared assert span2.name == 't2' assert span2.tags == { 'error': 'true', 'error.class': 'Exception', 'error.message': '', } assert 'raise Exception()' in span2.annotations[0].value
async def test_span_skip(): class TestAdapter(AbcAdapter): started = False stopped = False handled: List[Span] = [] async def start(self, logger: 'Logger'): self.started = True def handle(self, span: Span): self.handled.append(span) async def stop(self): self.stopped = True app = BaseApplication(BaseConfig()) lgr = app.logger lgr.add(TestAdapter()) adapter = lgr.adapters[TestAdapter.__name__] assert isinstance(adapter, TestAdapter) await lgr.start() assert adapter.started with lgr.span_new() as span: with span.new_child(): pass span.skip() with span.new_child(): pass assert len(adapter.handled) == 0 await lgr.stop() assert adapter.stopped assert len(adapter.handled) == 0
async def test_logger_adapter(): class TestAdapter(AbcAdapter): started = False stopped = False handled: List[Span] = [] async def start(self, logger: 'Logger'): self.started = True def handle(self, span: Span): self.handled.append(span) async def stop(self): self.stopped = True app = BaseApplication(BaseConfig()) lgr = app.logger lgr.add(TestAdapter()) adapter = lgr.adapters[0] assert isinstance(adapter, TestAdapter) await lgr.start() assert adapter.started with lgr.span_new() as span: span.tag('tag', 'abc') await lgr.stop() assert adapter.stopped assert len(adapter.handled) == 1 assert span.start_stamp is not None assert span.finish_stamp is not None assert 'tag' in span.tags assert span.tags['tag'] == 'abc'
async def test_logger_invalid_adapter(): app = BaseApplication(BaseConfig()) lgr = app.logger with pytest.raises(UserWarning): lgr.add(object())
async def test_dead_letter_exchange(loop, rabbitmq_url): messages: List[Tuple[bytes]] = [] queue: str class TestPubChg(PikaChannel): name = 'pub' async def prepare(self) -> None: await self.exchange_declare('myexchange1', durable=False) await self.queue_declare('myqueue1', durable=False) await self.queue_bind('myqueue1', 'myexchange1', '') q = await self.queue_declare( '', exclusive=True, arguments={ 'x-dead-letter-exchange': '', 'x-dead-letter-routing-key': 'myqueue1', }, ) self.queue = q.method.queue async def send(self, body: bytes, expiration: str): await self.publish( '', self.queue, body, BasicProperties(expiration=expiration) ) class TestCnsChg(PikaChannel): name = 'sub' async def prepare(self) -> None: await self.qos(prefetch_count=1) async def start(self) -> None: await self.consume('myqueue1', self.message) async def message( self, body: bytes, deliver: Deliver, proprties: Properties ) -> None: await self.ack(delivery_tag=deliver.delivery_tag) messages.append((body, proprties)) app = BaseApplication(BaseConfig()) app.add( 'mq', Pika( PikaConfig(url=rabbitmq_url), [ lambda: TestPubChg(PikaChannelConfig()), lambda: TestCnsChg(PikaChannelConfig()), ], ), ) await app.start() mq: Pika = app.get('mq') # type: ignore await mq.channel('pub').send(b'testmsg', '1') await wait_for(lambda: len(messages) > 0) assert messages[0][1].headers['x-death'][0]['count'] == 1 assert repr(messages[0][1].headers['x-death'][0]['count']) == '1L' await app.stop()
async def test_http(unused_tcp_port): class TestClient(Client): async def send(self, url: str) -> ClientResponse: return await self.request('GET', url) async def send_add_get(self, url: str) -> ClientResponse: return await self.request('GET', url) async def send_add_head(self, url: str) -> ClientResponse: return await self.request('HEAD', url) async def send_add_post(self, url: str) -> ClientResponse: return await self.request('POST', url) async def send_add_put(self, url: str) -> ClientResponse: return await self.request('PUT', url) async def send_add_patch(self, url: str) -> ClientResponse: return await self.request('PATCH', url) async def send_add_delete(self, url: str) -> ClientResponse: return await self.request('DELETE', url) class Handler(ServerHandler): async def prepare(self) -> None: self.server.add_route('GET', '/', self.home) self.server.add_head('/', self.test_head) self.server.add_get('/test_get', self.test_get) self.server.add_post('/test_post', self.test_post) self.server.add_patch('/test_patch', self.test_patch) self.server.add_put('/test_put', self.test_put) self.server.add_delete('/test_delete', self.test_delete) async def home(self, request: web.Request) -> web.Response: return web.Response(text='OK') async def test_get(self, request: web.Request) -> web.Response: body = request.method return web.Response(text=body) async def test_head(self, request: web.Request) -> web.Response: return web.Response() async def test_post(self, request: web.Request) -> web.Response: body = request.method return web.Response(text=body) async def test_patch(self, request: web.Request) -> web.Response: body = request.method return web.Response(text=body) async def test_put(self, request: web.Request) -> web.Response: body = request.method return web.Response(text=body) async def test_delete(self, request: web.Request) -> web.Response: body = request.method return web.Response(text=body) app = BaseApplication(BaseConfig()) app.add('srv', Server(ServerConfig(port=unused_tcp_port), Handler())) app.add('clt', TestClient()) url_test = 'http://127.0.0.1:%d' % unused_tcp_port await app.start() resp = await app.get('clt').send(url_test) assert resp.status == 200 assert await resp.text() == 'OK' resp_head = await app.get('clt').send_add_head(f'{url_test}/') assert resp_head.status == 200 resp_get = await app.get('clt').send_add_get(f'{url_test}/test_get') assert resp_get.status == 200 assert await resp_get.text() == 'GET' resp_post = await app.get('clt').send_add_post(f'{url_test}/test_post') assert resp_post.status == 200 assert await resp_post.text() == 'POST' resp_patch = await app.get('clt').send_add_patch(f'{url_test}/test_patch') assert resp_patch.status == 200 assert await resp_patch.text() == 'PATCH' resp_put = await app.get('clt').send_add_put(f'{url_test}/test_put') assert resp_put.status == 200 assert await resp_put.text() == 'PUT' resp_delete = await app.get('clt').send_add_delete( f'{url_test}/test_delete', ) assert resp_delete.status == 200 assert await resp_delete.text() == 'DELETE' await app.stop()
async def test_success( loop, ): async with ZipkinServer() as zs: cfg = ZipkinConfig(name='123', addr=zs.addr) adapter = ZipkinAdapter(cfg) app = BaseApplication(BaseConfig()) app.logger.add(adapter) lgr = app.logger await lgr.start() with lgr.span_new(name='t1', kind=Span.KIND_SERVER) as sp: sp.tag('tag', 'abc') sp.annotate('k1', 'val1', ts=123456) with pytest.raises(Exception): with sp.new_child('t2', Span.KIND_CLIENT): raise Exception() with lgr.span_new(kind=Span.KIND_SERVER) as sp3: sp3.set_name4adapter(lgr.ADAPTER_ZIPKIN, '111') sp3.annotate4adapter( lgr.ADAPTER_ZIPKIN, PgSpan.ANN_RESULT, json_encode({'result': '123'}), ts=122211000000, ) sp3.set_tag4adapter( lgr.ADAPTER_ZIPKIN, PgSpan.TAG_QUERY_NAME, 'get_paym', ) await lgr.stop() assert len(zs.spans) == 3 span: SpanModel = zs.spans[1] span2: SpanModel = zs.spans[0] span3: SpanModel = zs.spans[2] assert span.name == 't1' assert span.tags == {'tag': 'abc'} assert span.annotations == [ Annotation(value='val1', timestamp=123456000000) ] assert span.duration > 0 assert span.timestamp > 0 assert not span.debug assert span.shared assert span2.name == 't2' assert span2.tags == { 'error': 'true', 'error.class': 'Exception', 'error.message': '', } assert 'raise Exception()' in span2.annotations[0].value assert span3.name == '111' assert span3.tags == {PgSpan.TAG_QUERY_NAME: 'get_paym'} assert span3.annotations == [ Annotation(value='{"result": "123"}', timestamp=122211000000000000) ]
def get_app() -> BaseApplication: return BaseApplication(BaseConfig())
async def test_span(): class TestAdapter(AbcAdapter): started = False stopped = False handled: List[Span] = [] async def start(self, logger: 'Logger'): self.started = True def handle(self, span: Span): self.handled.append(span) async def stop(self): self.stopped = True app = BaseApplication(BaseConfig()) lgr = app.logger lgr.add(TestAdapter()) adapter = lgr.adapters[TestAdapter.__name__] assert isinstance(adapter, TestAdapter) await lgr.start() assert adapter.started exc = Exception('Err') exc2 = Exception('Err2') span = Span(lgr, '1234') assert span.duration == 0 with lgr.span_new('t1', kind=Span.KIND_SERVER) as span: dur1 = span.duration assert dur1 > 0 assert span.kind == span.KIND_SERVER assert span.name == 't1' with span.new_child(name='t2', kind=Span.KIND_CLIENT) as span2: assert span2.kind == span.KIND_CLIENT assert span2.name == 't2' assert span2.parent_id == span.id assert span2.parent is span try: raise exc except Exception as err: span2.error(err) try: with span2.new_child(name='t3', kind=Span.KIND_CLIENT) as span3: assert span3.parent_id == span2.id assert span3.parent is span2 raise exc2 except Exception as err2: assert err2 is exc2 assert span.duration > dur1 span.set_name4adapter(TestAdapter.name, 'tt1') span.annotate('k1', '1', ts=1) span.annotate('k1', '2', ts=2) span.annotate4adapter(TestAdapter.name, 'k2', '2', ts=2) span.annotate4adapter(TestAdapter.name, 'k2', '3', ts=3) span.tag('tag1', '11') span.set_tag4adapter(TestAdapter.name, 'tag2', '22') span.set_tag4adapter(TestAdapter.name, 'tag3', '33') await lgr.stop() assert adapter.stopped assert len(adapter.handled) == 3 span = adapter.handled[2] span2 = adapter.handled[1] span3 = adapter.handled[0] assert span.kind == span.KIND_SERVER assert span2.kind == span.KIND_CLIENT assert span.duration > 0 # name assert span.name == 't1' assert span2.name == 't2' assert span.get_name4adapter(TestAdapter.name) == 'tt1' assert span.get_name4adapter(TestAdapter.name, merge=False) == 'tt1' assert span2.get_name4adapter(TestAdapter.name) == 't2' assert span2.get_name4adapter(TestAdapter.name, merge=False) is None # tags assert span.tags == {'tag1': '11'} assert span.get_tags4adapter(TestAdapter.name) == { 'tag1': '11', 'tag2': '22', 'tag3': '33', } assert span.get_tags4adapter(TestAdapter.name, merge=False) == { 'tag2': '22', 'tag3': '33', } assert span.get_tags4adapter('unknown') == {'tag1': '11'} assert span.get_tags4adapter('unknown', merge=False) == {} # annotations assert span.annotations == {'k1': [('1', 1), ('2', 2)]} assert span.get_annotations4adapter(TestAdapter.name) == { 'k1': [('1', 1), ('2', 2)], 'k2': [('2', 2), ('3', 3)], } assert span.get_annotations4adapter(TestAdapter.name, merge=False) == { 'k2': [('2', 2), ('3', 3)] } assert span.get_annotations4adapter('unknown...') == { 'k1': [('1', 1), ('2', 2)] } assert span.get_annotations4adapter('unknown...', merge=False) == {} # log error assert span2.tags == { 'error': 'true', 'error.class': 'Exception', 'error.message': 'Err', } assert 'traceback' in span2.annotations assert len(span2.annotations['traceback']) == 1 assert isinstance(span2.annotations['traceback'][0][0], str) assert len(span2.annotations['traceback'][0][0]) > 0 assert span2.get_error() is exc assert span3.tags == { 'error': 'true', 'error.class': 'Exception', 'error.message': 'Err2', } assert 'traceback' in span3.annotations assert len(span3.annotations['traceback']) == 1 assert isinstance(span3.annotations['traceback'][0][0], str) assert len(span3.annotations['traceback'][0][0]) > 0 assert span3.get_error() is exc2 assert 'Span' in str(span) assert 'ms' in str(span)