@@ -73,6 +73,14 @@ async def echo_body(request: Request):
7373 return await request .body ()
7474
7575
76+ async def websocket_endpoint (websocket : WebSocket , name : str ):
77+ logfire .info ('websocket_endpoint: {name}' , name = name )
78+ await websocket .accept ()
79+ assert (await websocket .receive_text ()) == 'ping'
80+ await websocket .send_text ('pong' )
81+ await websocket .close ()
82+
83+
7684@pytest .fixture ()
7785def app ():
7886 # Don't define the endpoint functions in this fixture to prevent a qualname with <locals> in it
@@ -90,6 +98,7 @@ def app():
9098 app .get ('/validation_error' )(validation_error )
9199 app .get ('/with_path_param/{param}' )(with_path_param )
92100 app .get ('/secret/{path_param}' , name = 'secret' )(get_secret )
101+ app .websocket ('/ws/{name}' )(websocket_endpoint )
93102 first_lvl_app .get ('/other' , name = 'other_route_name' , operation_id = 'other_route_operation_id' )(other_route )
94103 second_lvl_app .get ('/other' , name = 'other_route_name' , operation_id = 'other_route_operation_id' )(other_route )
95104 return app
@@ -1818,3 +1827,144 @@ def test_request_hooks_with_send_receive_spans(exporter: TestExporter):
18181827 },
18191828 ]
18201829 )
1830+
1831+
1832+ def test_websocket (client : TestClient , exporter : TestExporter ) -> None :
1833+ with client .websocket_connect ('/ws/foo' ) as websocket :
1834+ websocket .send_text ('ping' )
1835+ data = websocket .receive_text ()
1836+ assert data == 'pong'
1837+
1838+ assert exporter .exported_spans_as_dict () == snapshot (
1839+ [
1840+ {
1841+ 'name' : 'FastAPI arguments' ,
1842+ 'context' : {'trace_id' : 1 , 'span_id' : 3 , 'is_remote' : False },
1843+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1844+ 'start_time' : 2000000000 ,
1845+ 'end_time' : 3000000000 ,
1846+ 'attributes' : {
1847+ 'logfire.msg_template' : 'FastAPI arguments' ,
1848+ 'logfire.msg' : 'FastAPI arguments' ,
1849+ 'logfire.span_type' : 'span' ,
1850+ 'logfire.level_num' : 5 ,
1851+ },
1852+ },
1853+ {
1854+ 'name' : 'websocket_endpoint: {name}' ,
1855+ 'context' : {'trace_id' : 1 , 'span_id' : 5 , 'is_remote' : False },
1856+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1857+ 'start_time' : 4000000000 ,
1858+ 'end_time' : 4000000000 ,
1859+ 'attributes' : {
1860+ 'logfire.span_type' : 'log' ,
1861+ 'logfire.level_num' : 9 ,
1862+ 'logfire.msg_template' : 'websocket_endpoint: {name}' ,
1863+ 'logfire.msg' : 'websocket_endpoint: foo' ,
1864+ 'code.filepath' : 'test_fastapi.py' ,
1865+ 'code.function' : 'websocket_endpoint' ,
1866+ 'code.lineno' : 123 ,
1867+ 'name' : 'foo' ,
1868+ 'logfire.json_schema' : '{"type":"object","properties":{"name":{}}}' ,
1869+ },
1870+ },
1871+ {
1872+ 'name' : 'HTTP /ws/{name} websocket receive connect' ,
1873+ 'context' : {'trace_id' : 1 , 'span_id' : 6 , 'is_remote' : False },
1874+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1875+ 'start_time' : 5000000000 ,
1876+ 'end_time' : 6000000000 ,
1877+ 'attributes' : {
1878+ 'logfire.span_type' : 'span' ,
1879+ 'logfire.msg' : 'HTTP /ws/{name} websocket receive connect' ,
1880+ 'logfire.level_num' : 5 ,
1881+ 'asgi.event.type' : 'websocket.connect' ,
1882+ },
1883+ },
1884+ {
1885+ 'name' : 'HTTP /ws/{name} websocket send accept' ,
1886+ 'context' : {'trace_id' : 1 , 'span_id' : 8 , 'is_remote' : False },
1887+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1888+ 'start_time' : 7000000000 ,
1889+ 'end_time' : 8000000000 ,
1890+ 'attributes' : {
1891+ 'logfire.span_type' : 'span' ,
1892+ 'logfire.msg' : 'HTTP /ws/{name} websocket send accept' ,
1893+ 'logfire.level_num' : 5 ,
1894+ 'asgi.event.type' : 'websocket.accept' ,
1895+ },
1896+ },
1897+ {
1898+ 'name' : 'HTTP /ws/{name} websocket receive' ,
1899+ 'context' : {'trace_id' : 1 , 'span_id' : 10 , 'is_remote' : False },
1900+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1901+ 'start_time' : 9000000000 ,
1902+ 'end_time' : 10000000000 ,
1903+ 'attributes' : {
1904+ 'logfire.span_type' : 'span' ,
1905+ 'logfire.msg' : 'HTTP /ws/{name} websocket receive' ,
1906+ 'logfire.level_num' : 5 ,
1907+ 'http.status_code' : 200 ,
1908+ 'http.response.status_code' : 200 ,
1909+ 'asgi.event.type' : 'websocket.receive' ,
1910+ },
1911+ },
1912+ {
1913+ 'name' : 'HTTP /ws/{name} websocket send' ,
1914+ 'context' : {'trace_id' : 1 , 'span_id' : 12 , 'is_remote' : False },
1915+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1916+ 'start_time' : 11000000000 ,
1917+ 'end_time' : 12000000000 ,
1918+ 'attributes' : {
1919+ 'logfire.span_type' : 'span' ,
1920+ 'logfire.msg' : 'HTTP /ws/{name} websocket send' ,
1921+ 'logfire.level_num' : 5 ,
1922+ 'asgi.event.type' : 'websocket.send' ,
1923+ 'http.status_code' : 200 ,
1924+ 'http.response.status_code' : 200 ,
1925+ },
1926+ },
1927+ {
1928+ 'name' : 'HTTP /ws/{name} websocket send close' ,
1929+ 'context' : {'trace_id' : 1 , 'span_id' : 14 , 'is_remote' : False },
1930+ 'parent' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1931+ 'start_time' : 13000000000 ,
1932+ 'end_time' : 14000000000 ,
1933+ 'attributes' : {
1934+ 'logfire.span_type' : 'span' ,
1935+ 'logfire.msg' : 'HTTP /ws/{name} websocket send close' ,
1936+ 'logfire.level_num' : 5 ,
1937+ 'asgi.event.type' : 'websocket.close' ,
1938+ },
1939+ },
1940+ {
1941+ 'name' : 'HTTP /ws/{name}' ,
1942+ 'context' : {'trace_id' : 1 , 'span_id' : 1 , 'is_remote' : False },
1943+ 'parent' : None ,
1944+ 'start_time' : 1000000000 ,
1945+ 'end_time' : 15000000000 ,
1946+ 'attributes' : {
1947+ 'logfire.span_type' : 'span' ,
1948+ 'logfire.msg' : 'HTTP /ws/foo' ,
1949+ 'http.scheme' : 'ws' ,
1950+ 'url.scheme' : 'ws' ,
1951+ 'http.host' : 'testserver' ,
1952+ 'client.address' : 'testserver' ,
1953+ 'net.host.port' : 80 ,
1954+ 'server.port' : 80 ,
1955+ 'http.target' : '/ws/foo' ,
1956+ 'url.path' : '/ws/foo' ,
1957+ 'http.url' : 'ws://testserver/ws/foo' ,
1958+ 'http.server_name' : 'testserver' ,
1959+ 'http.user_agent' : 'testclient' ,
1960+ 'user_agent.original' : 'testclient' ,
1961+ 'net.peer.ip' : 'testclient' ,
1962+ 'net.peer.port' : 50000 ,
1963+ 'client.port' : 50000 ,
1964+ 'http.route' : '/ws/{name}' ,
1965+ 'http.status_code' : 200 ,
1966+ 'http.response.status_code' : 200 ,
1967+ },
1968+ },
1969+ ]
1970+ )
0 commit comments