Skip to content

Commit

Permalink
Check token in separate thread (#274)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmojaki authored Jun 21, 2024
1 parent b1adb4d commit 0829bee
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 21 deletions.
22 changes: 13 additions & 9 deletions logfire/_internal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
from threading import RLock
from threading import RLock, Thread
from typing import Any, Callable, Literal, Sequence, cast
from urllib.parse import urljoin
from uuid import uuid4
Expand Down Expand Up @@ -637,7 +637,6 @@ def add_span_processor(span_processor: SpanProcessor) -> None:
metric_readers = list(self.additional_metric_readers or [])

if (self.send_to_logfire == 'if-token-present' and self.token is not None) or self.send_to_logfire is True:
credentials: LogfireCredentials | None = None
if self.token is None:
if (credentials := LogfireCredentials.load_creds_file(self.data_dir)) is None: # pragma: no branch
credentials = LogfireCredentials.initialize_project(
Expand All @@ -647,15 +646,19 @@ def add_span_processor(span_processor: SpanProcessor) -> None:
)
credentials.write_creds_file(self.data_dir)
self.token = credentials.token
self.project_name = credentials.project_name
self.base_url = self.base_url or credentials.logfire_api_url
if self.show_summary: # pragma: no branch
credentials.print_token_summary()
else:
credentials = self._initialize_credentials_from_token(self.token)
if credentials is not None:
self.project_name = credentials.project_name

if self.show_summary and credentials is not None: # pragma: no cover
credentials.print_token_summary()
def check_token():
assert self.token is not None
creds = self._initialize_credentials_from_token(self.token)
if self.show_summary and creds is not None: # pragma: no branch
creds.print_token_summary()

thread = Thread(target=check_token, name='check_logfire_token')
thread.start()

headers = {'User-Agent': f'logfire/{VERSION}', 'Authorization': self.token}
self.logfire_api_session.headers.update(headers)
Expand Down Expand Up @@ -843,7 +846,8 @@ def from_token(cls, token: str, session: requests.Session, base_url: str) -> Sel
return None

if response.status_code == 401:
raise LogfireConfigError('Invalid Logfire token.')
warnings.warn('Invalid Logfire token.')
return None
elif response.status_code != 200:
# any other status code is considered unhealthy
warnings.warn(
Expand Down
42 changes: 30 additions & 12 deletions tests/test_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
import os
import sys
import threading
from contextlib import ExitStack
from pathlib import Path
from typing import Any, Sequence
Expand Down Expand Up @@ -533,10 +534,12 @@ def default_span_processor(exporter: SpanExporter) -> SimpleSpanProcessor:
logfire.configure(
send_to_logfire=True,
data_dir=data_dir,
token='abc',
token='abc1',
default_span_processor=default_span_processor,
additional_metric_readers=[InMemoryMetricReader()],
collect_system_metrics=False,
)
wait_for_check_token_thread()

assert not data_dir.exists()
path = data_dir / 'logfire_spans.bin'
Expand All @@ -561,16 +564,18 @@ def test_configure_service_version(tmp_path: str) -> None:

with request_mocker:
configure(
token='abc',
token='abc2',
service_version='1.2.3',
additional_metric_readers=[InMemoryMetricReader()],
collect_system_metrics=False,
)

assert GLOBAL_CONFIG.service_version == '1.2.3'

configure(
token='abc',
token='abc3',
additional_metric_readers=[InMemoryMetricReader()],
collect_system_metrics=False,
)

assert GLOBAL_CONFIG.service_version == git_sha
Expand All @@ -580,13 +585,16 @@ def test_configure_service_version(tmp_path: str) -> None:
try:
os.chdir(tmp_path)
configure(
token='abc',
token='abc4',
additional_metric_readers=[InMemoryMetricReader()],
collect_system_metrics=False,
)
assert GLOBAL_CONFIG.service_version is None
finally:
os.chdir(dir)

wait_for_check_token_thread()


def test_otel_service_name_env_var() -> None:
time_generator = TimeGenerator()
Expand Down Expand Up @@ -838,7 +846,7 @@ def test_initialize_project_use_existing_project_no_projects(tmp_dir_cwd: Path,
}
request_mocker.post('https://logfire-api.pydantic.dev/v1/projects/fake_org', [create_project_response])

logfire.configure(send_to_logfire=True)
logfire.configure(send_to_logfire=True, collect_system_metrics=False)

assert confirm_mock.mock_calls == [
call('The project will be created in the organization "fake_org". Continue?', default=True),
Expand Down Expand Up @@ -873,7 +881,7 @@ def test_initialize_project_use_existing_project(tmp_dir_cwd: Path, tmp_path: Pa
[create_project_response],
)

logfire.configure(send_to_logfire=True)
logfire.configure(send_to_logfire=True, collect_system_metrics=False)

assert confirm_mock.mock_calls == [
call('Do you want to use one of your existing projects? ', default=True),
Expand Down Expand Up @@ -930,7 +938,10 @@ def test_initialize_project_not_using_existing_project(
[create_project_response],
)

logfire.configure(send_to_logfire=True)
logfire.configure(
send_to_logfire=True,
collect_system_metrics=False,
)

assert confirm_mock.mock_calls == [
call('Do you want to use one of your existing projects? ', default=True),
Expand Down Expand Up @@ -970,7 +981,7 @@ def test_initialize_project_not_confirming_organization(tmp_path: Path) -> None:
)

with pytest.raises(SystemExit):
logfire.configure(data_dir=tmp_path, send_to_logfire=True)
logfire.configure(data_dir=tmp_path, send_to_logfire=True, collect_system_metrics=False)

assert confirm_mock.mock_calls == [
call('Do you want to use one of your existing projects? ', default=True),
Expand Down Expand Up @@ -1047,7 +1058,7 @@ def test_initialize_project_create_project(tmp_dir_cwd: Path, tmp_path: Path, ca
],
)

logfire.configure(send_to_logfire=True)
logfire.configure(send_to_logfire=True, collect_system_metrics=False)

for request in request_mocker.request_history:
assert request.headers['Authorization'] == 'fake_user_token'
Expand Down Expand Up @@ -1130,7 +1141,7 @@ def test_initialize_project_create_project_default_organization(tmp_dir_cwd: Pat
[create_project_response],
)

logfire.configure(send_to_logfire=True)
logfire.configure(send_to_logfire=True, collect_system_metrics=False)

assert prompt_mock.mock_calls == [
call(
Expand Down Expand Up @@ -1162,7 +1173,7 @@ def test_send_to_logfire_true(tmp_path: Path) -> None:
)
)
with pytest.raises(RuntimeError, match='^expected$'):
configure(send_to_logfire=True, console=False, data_dir=data_dir)
configure(send_to_logfire=True, console=False, data_dir=data_dir, collect_system_metrics=False)


def test_send_to_logfire_false() -> None:
Expand Down Expand Up @@ -1190,6 +1201,12 @@ def test_send_to_logfire_if_token_present_empty() -> None:
del os.environ['LOGFIRE_TOKEN']


def wait_for_check_token_thread():
for thread in threading.enumerate():
if thread.name == 'check_logfire_token': # pragma: no cover
thread.join()


def test_send_to_logfire_if_token_present_not_empty(capsys: pytest.CaptureFixture[str]) -> None:
os.environ['LOGFIRE_TOKEN'] = 'foobar'
try:
Expand All @@ -1199,6 +1216,7 @@ def test_send_to_logfire_if_token_present_not_empty(capsys: pytest.CaptureFixtur
json={'project_name': 'myproject', 'project_url': 'fake_project_url'},
)
configure(send_to_logfire='if-token-present', console=False)
wait_for_check_token_thread()
assert len(request_mocker.request_history) == 1
assert capsys.readouterr().err == 'Logfire project URL: fake_project_url\n'
finally:
Expand Down Expand Up @@ -1261,7 +1279,7 @@ def test_initialize_credentials_from_token_invalid_token():
stack.enter_context(request_mocker)
request_mocker.get('https://logfire-api.pydantic.dev/v1/info', text='Error', status_code=401)

with pytest.raises(LogfireConfigError, match='Invalid Logfire token.'):
with pytest.warns(match='Invalid Logfire token.'):
LogfireConfig()._initialize_credentials_from_token('some-token') # type: ignore


Expand Down

0 comments on commit 0829bee

Please sign in to comment.