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

Add CodeSource parameter #409

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
52 changes: 52 additions & 0 deletions logfire/_internal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
from .constants import (
DEFAULT_FALLBACK_FILE_NAME,
OTLP_MAX_BODY_SIZE,
RESOURCE_ATTRIBUTES_CODE_GIT_REF,
RESOURCE_ATTRIBUTES_CODE_ROOT_PATH,
RESOURCE_ATTRIBUTES_REPO_URL,
LevelName,
)
from .exporters.console import (
Expand Down Expand Up @@ -144,6 +147,28 @@
"""Exclude specific modules from instrumentation."""


@dataclass
class CodeSource:
"""Settings for the source code of the project."""

repository: str
"""The repository URL for the code e.g. https://github.com/pydantic/logfire"""

git_ref: str
"""The git reference for the code i.e. branch name, commit hash or tag."""

root_path: str | None = None
"""The root path for the source code in the repository.

Example:
If the `code.filename` is `/path/to/project/src/logfire/main.py` and the `root_path` is `src/`, the URL
for the source code will be `src/path/to/project/src/logfire/main.py`.
"""

provider: Literal['github'] = 'github'
"""The provider for the repository. This is used to build the URL for the source code."""


def configure(
*,
send_to_logfire: bool | Literal['if-token-present'] | None = None,
Expand Down Expand Up @@ -171,6 +196,7 @@
scrubbing: ScrubbingOptions | Literal[False] | None = None,
inspect_arguments: bool | None = None,
tail_sampling: TailSamplingOptions | None = None,
code_source: CodeSource | None = None,
) -> None:
"""Configure the logfire SDK.

Expand Down Expand Up @@ -218,6 +244,7 @@
If `None` uses the `LOGFIRE_INSPECT_ARGUMENTS` environment variable.
Defaults to `True` if and only if the Python version is at least 3.11.
tail_sampling: Tail sampling options. Not ready for general use.
code_source: Settings for the source code of the project. Defaults to `None`.
"""
if processors is not None: # pragma: no cover
raise ValueError(
Expand Down Expand Up @@ -277,6 +304,7 @@
scrubbing=scrubbing,
inspect_arguments=inspect_arguments,
tail_sampling=tail_sampling,
code_source=code_source,
)


Expand Down Expand Up @@ -353,6 +381,9 @@
tail_sampling: TailSamplingOptions | None
"""Tail sampling options"""

code_source: CodeSource | None
"""Settings for the source code of the project"""

def _load_configuration(
self,
# note that there are no defaults here so that the only place
Expand All @@ -378,6 +409,7 @@
scrubbing: ScrubbingOptions | Literal[False] | None,
inspect_arguments: bool | None,
tail_sampling: TailSamplingOptions | None,
code_source: CodeSource | None,
) -> None:
"""Merge the given parameters with the environment variables file configurations."""
param_manager = ParamManager.create(config_dir)
Expand Down Expand Up @@ -440,6 +472,11 @@
tail_sampling = TailSamplingOptions(**tail_sampling) # type: ignore
self.tail_sampling = tail_sampling

if isinstance(code_source, dict):
# This is particularly for deserializing from a dict as in executors.py
code_source = CodeSource(**code_source) # type: ignore

Check warning on line 477 in logfire/_internal/config.py

View check run for this annotation

Codecov / codecov/patch

logfire/_internal/config.py#L477

Added line #L477 was not covered by tests
self.code_source = code_source

self.fast_shutdown = fast_shutdown

self.id_generator = id_generator or RandomIdGenerator()
Expand Down Expand Up @@ -478,6 +515,7 @@
scrubbing: ScrubbingOptions | Literal[False] | None = None,
inspect_arguments: bool | None = None,
tail_sampling: TailSamplingOptions | None = None,
code_source: CodeSource | None = None,
) -> None:
"""Create a new LogfireConfig.

Expand Down Expand Up @@ -508,6 +546,7 @@
scrubbing=scrubbing,
inspect_arguments=inspect_arguments,
tail_sampling=tail_sampling,
code_source=code_source,
)
# initialize with no-ops so that we don't impact OTEL's global config just because logfire is installed
# that is, we defer setting logfire as the otel global config until `configure` is called
Expand Down Expand Up @@ -542,6 +581,7 @@
scrubbing: ScrubbingOptions | Literal[False] | None,
inspect_arguments: bool | None,
tail_sampling: TailSamplingOptions | None,
code_source: CodeSource | None,
) -> None:
with self._lock:
self._initialized = False
Expand All @@ -566,6 +606,7 @@
scrubbing,
inspect_arguments,
tail_sampling,
code_source,
)
self.initialize()

Expand All @@ -586,6 +627,17 @@
# disabled for now, but we may want to re-enable something like it in the future
# RESOURCE_ATTRIBUTES_PACKAGE_VERSIONS: json.dumps(collect_package_info(), separators=(',', ':')),
}
if self.code_source:
if self.code_source.provider != 'github':
raise LogfireConfigError('Only GitHub repositories are supported for code')
otel_resource_attributes.update(

Check warning on line 633 in logfire/_internal/config.py

View check run for this annotation

Codecov / codecov/patch

logfire/_internal/config.py#L632-L633

Added lines #L632 - L633 were not covered by tests
{
RESOURCE_ATTRIBUTES_CODE_GIT_REF: self.code_source.git_ref,
RESOURCE_ATTRIBUTES_CODE_ROOT_PATH: self.code_source.root_path,
RESOURCE_ATTRIBUTES_REPO_URL: self.code_source.repository,
}
)

if self.service_version:
otel_resource_attributes[ResourceAttributes.SERVICE_VERSION] = self.service_version
otel_resource_attributes_from_env = os.getenv(OTEL_RESOURCE_ATTRIBUTES)
Expand Down
9 changes: 9 additions & 0 deletions logfire/_internal/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ def log_level_attributes(level: LevelName | int) -> dict[str, otel_types.Attribu
RESOURCE_ATTRIBUTES_PACKAGE_VERSIONS = 'logfire.package_versions'
"""Versions of installed packages, serialized as list of json objects with keys 'name' and 'version'."""

RESOURCE_ATTRIBUTES_CODE_GIT_REF = 'logfire.code.git_ref'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general we should try following semantic conventions where they exist. With a bit of effort I found https://opentelemetry.io/docs/specs/semconv/attributes-registry/vcs/. This would be vcs.repository.ref.revision.

Just noting that, not saying we should go with this overall approach or spend time on this now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't find it. Nice. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are experimental. They are not included in the opentelemetry.semconv package yet. What should I do?

"""The git ref of the current repository."""

RESOURCE_ATTRIBUTES_CODE_ROOT_PATH = 'logfire.code.root_path'
"""The root path of the current repository."""

RESOURCE_ATTRIBUTES_REPO_URL = 'logfire.code.repo_url'
"""The URL of the current repository."""

OTLP_MAX_INT_SIZE = 2**63 - 1
"""OTLP only supports signed 64-bit integers, larger integers get sent as strings."""

Expand Down
Loading