Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support 'if-token-present' for env var 'LOGFIRE_SEND_TO_LOGFIRE' #488

Merged
merged 9 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
11 changes: 6 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ We'd love anyone interested to contribute to the Logfire SDK and documentation.

1. Fork and clone the repository
2. [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
3. Run `make install` to install dependencies
4. Run `make test` to run unit tests
5. Run `make format` to format code
6. Run `make lint` to lint code
7. run `make docs` to build docs and `make docs-serve` to serve docs locally
3. [Install pre-commit](https://pre-commit.com/#install)
4. Run `make install` to install dependencies
5. Run `make test` to run unit tests
6. Run `make format` to format code
7. Run `make lint` to lint code
8. run `make docs` to build docs and `make docs-serve` to serve docs locally

You're now set up to start contributing!
2 changes: 1 addition & 1 deletion logfire-api/logfire_api/_internal/config.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class _LogfireConfigData:
advanced: AdvancedOptions

class LogfireConfig(_LogfireConfigData):
def __init__(self, send_to_logfire: bool | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | None = None, data_dir: Path | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, code_source: CodeSource | None = None, advanced: AdvancedOptions | None = None) -> None:
def __init__(self, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | None = None, data_dir: Path | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, code_source: CodeSource | None = None, advanced: AdvancedOptions | None = None) -> None:
"""Create a new LogfireConfig.

Users should never need to call this directly, instead use `logfire.configure`.
Expand Down
2 changes: 1 addition & 1 deletion logfire/_internal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ def _load_configuration(
class LogfireConfig(_LogfireConfigData):
def __init__(
self,
send_to_logfire: bool | None = None,
send_to_logfire: bool | Literal['if-token-present'] | None = None,
token: str | None = None,
service_name: str | None = None,
service_version: str | None = None,
Expand Down
11 changes: 9 additions & 2 deletions logfire/_internal/config_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from typing import Any, Callable, Literal, Set, TypeVar
from typing import Any, Callable, Literal, Set, TypeVar, Union

from opentelemetry.sdk.environment_variables import OTEL_SERVICE_NAME
from typing_extensions import get_args, get_origin
Expand Down Expand Up @@ -53,7 +53,7 @@ class _DefaultCallback:
"""When running under pytest, don't send spans to Logfire by default."""

# fmt: off
SEND_TO_LOGFIRE = ConfigParam(env_vars=['LOGFIRE_SEND_TO_LOGFIRE'], allow_file_config=True, default=_send_to_logfire_default, tp=bool)
SEND_TO_LOGFIRE = ConfigParam(env_vars=['LOGFIRE_SEND_TO_LOGFIRE'], allow_file_config=True, default=_send_to_logfire_default, tp=Union[bool, Literal['if-token-present']])
"""Whether to send spans to Logfire."""
TOKEN = ConfigParam(env_vars=['LOGFIRE_TOKEN'])
"""Token for the Logfire API."""
Expand Down Expand Up @@ -183,6 +183,13 @@ def _cast(self, value: Any, name: str, tp: type[T]) -> T | None:
return value
if get_origin(tp) is Literal:
return _check_literal(value, name, tp)
if get_origin(tp) is Union:
for arg in get_args(tp):
try:
return self._cast(value, name, arg)
except LogfireConfigError:
pass
raise LogfireConfigError(f'Expected {name} to be one of {get_args(tp)}, got {value!r}')
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
if tp is bool:
return _check_bool(value, name) # type: ignore
if tp is float:
Expand Down
21 changes: 21 additions & 0 deletions tests/test_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,13 @@ def test_read_config_from_environment_variables() -> None:
):
fresh_pydantic_plugin()

with patch.dict(os.environ, {'LOGFIRE_SEND_TO_LOGFIRE': 'not-valid'}):
with pytest.raises(
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
LogfireConfigError,
match="Expected send_to_logfire to be one of \\(<class 'bool'>, typing.Literal\\['if-token-present'\\]\\), got 'not-valid'",
):
configure()

assert fresh_pydantic_plugin().include == set()
with patch.dict(os.environ, {'LOGFIRE_PYDANTIC_PLUGIN_INCLUDE': 'test'}):
assert fresh_pydantic_plugin().include == {'test'}
Expand Down Expand Up @@ -1254,6 +1261,20 @@ def test_send_to_logfire_if_token_present_empty() -> None:
del os.environ['LOGFIRE_TOKEN']


def test_send_to_logfire_if_token_present_empty_via_env_var() -> None:
os.environ['LOGFIRE_TOKEN'] = ''
os.environ['LOGFIRE_SEND_TO_LOGFIRE'] = 'if-token-present'
try:
with ExitStack() as stack:
stack.enter_context(mock.patch('logfire._internal.config.Confirm.ask', side_effect=RuntimeError))
requests_mocker = stack.enter_context(requests_mock.Mocker())
configure(console=False)
assert len(requests_mocker.request_history) == 0
finally:
del os.environ['LOGFIRE_TOKEN']
del os.environ['LOGFIRE_SEND_TO_LOGFIRE']
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved


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