Skip to content

Commit 1fab306

Browse files
authored
Cherry-pick: feat: better logger stream allocation (codegen-sh#1006)
# Motivation <!-- Why is this change necessary? --> # Content <!-- Please include a summary of the change --> # Testing <!-- How was the change tested? --> # Please check the following before marking your PR as ready for review - [ ] I have added tests for my changes - [ ] I have updated the documentation or added new documentation as needed
2 parents 86d5013 + 8bcc267 commit 1fab306

File tree

9 files changed

+641
-921
lines changed

9 files changed

+641
-921
lines changed

codegen-examples/uv.lock

Lines changed: 223 additions & 596 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/changelog/changelog.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,41 @@ icon: "clock"
44
iconType: "solid"
55
---
66

7+
<Update label="v0.52.31" description="April 02, 2025">
8+
### [Updates dependency sentry-sdk.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.31)
9+
- Update dependency sentry-sdk to v2.25.1
10+
</Update>
11+
12+
<Update label="v0.52.30" description="April 01, 2025">
13+
### [Improves debugging with git init failure logs.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.30)
14+
- Add logs for git initialization failure
15+
</Update>
16+
17+
<Update label="v0.52.29" description="April 01, 2025">
18+
### [Fixes codebase initialization issue.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.29)
19+
- Fix issue with codebase initialization
20+
</Update>
21+
22+
<Update label="v0.52.28" description="March 31, 2025">
23+
### [Updates OpenAI dependency.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.28)
24+
- Update OpenAI dependency to v1.70.0
25+
</Update>
26+
27+
<Update label="v0.52.27" description="March 31, 2025">
28+
### [Upgrade sentry-sdk dependency.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.27)
29+
- Upgrade sentry-sdk dependency for better bug tracking
30+
</Update>
31+
32+
<Update label="v0.52.26" description="March 27, 2025">
33+
### [Updates openai dependency to the latest version.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.26)
34+
- Update dependency openai to version 1.69.0
35+
</Update>
36+
37+
<Update label="v0.52.25" description="March 26, 2025">
38+
### [remove unnecessary transaction in build_graph.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.25)
39+
- Remove build_graph transaction
40+
</Update>
41+
742
<Update label="v0.52.24" description="March 25, 2025">
843
### [Documentation update for `codegen create` command.](https://github.com/codegen-sh/codegen-sdk/releases/tag/v0.52.24)
944
- Update documentation for `codegen create` command

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
# renovate: datasource=python-version depName=python
77
requires-python = ">=3.12, <3.14"
88
dependencies = [
9-
"openai==1.68.2",
9+
"openai==1.70.0",
1010
"tiktoken<1.0.0,>=0.5.1",
1111
"tabulate>=0.9.0,<1.0.0",
1212
"codeowners<1.0.0,>=0.6.0",
@@ -35,7 +35,7 @@ dependencies = [
3535
"rustworkx>=0.15.1",
3636
"typing-extensions>=4.12.2",
3737
"termcolor>=2.4.0",
38-
"sentry-sdk==2.24.1",
38+
"sentry-sdk==2.25.1",
3939
"click>=8.1.7",
4040
"requests>=2.32.3",
4141
"lazy-object-proxy>=0.0.0",
@@ -127,7 +127,7 @@ dev-dependencies = [
127127
# "scalene>=1.5.45",
128128
"filelock<4.0.0,>=3.15.4",
129129
"pytest>=8.3.3",
130-
"pytest-cov>=6.0.0,<6.0.1",
130+
"pytest-cov>=6.1.0,<6.1.1",
131131
"ruff>=0.6.8",
132132
"mypy[mypyc,faster-cache]>=1.13.0",
133133
"pre-commit>=4.0.1",

src/codegen/git/repo_operator/repo_operator.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,7 @@ def create_from_repo(cls, repo_path: str, url: str, access_token: str | None = N
897897
if local_head.hexsha == remote_head.hexsha:
898898
return cls(repo_config=RepoConfig.from_repo_path(repo_path), bot_commit=False, access_token=access_token)
899899
except Exception:
900-
# If any git operations fail, fallback to fresh clone
901-
pass
900+
logger.exception("Failed to initialize Git repository. Falling back to fresh clone.")
902901

903902
# If we get here, repo exists but is not up to date or valid
904903
# Remove the existing directory to do a fresh clone
@@ -916,7 +915,6 @@ def create_from_repo(cls, repo_path: str, url: str, access_token: str | None = N
916915
# Initialize with the cloned repo
917916
git_cli = GitCLI(repo_path)
918917
except (GitCommandError, ValueError) as e:
919-
logger.exception("Failed to initialize Git repository:")
920-
logger.exception("Please authenticate with a valid token and ensure the repository is properly initialized.")
918+
logger.exception("Failed to initialize Git repository")
921919
return None
922920
return cls(repo_config=RepoConfig.from_repo_path(repo_path), bot_commit=False, access_token=access_token)

src/codegen/sdk/core/codebase.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from codegen.configs.models.codebase import CodebaseConfig, PinkMode
2626
from codegen.configs.models.secrets import SecretsConfig
2727
from codegen.git.repo_operator.repo_operator import RepoOperator
28-
from codegen.git.schemas.enums import CheckoutResult, SetupOption
28+
from codegen.git.schemas.enums import CheckoutResult
2929
from codegen.git.schemas.repo_config import RepoConfig
3030
from codegen.git.utils.pr_review import CodegenPR
3131
from codegen.sdk._proxy import proxy_property
@@ -1337,7 +1337,6 @@ def from_repo(
13371337
language: Literal["python", "typescript"] | ProgrammingLanguage | None = None,
13381338
config: CodebaseConfig | None = None,
13391339
secrets: SecretsConfig | None = None,
1340-
setup_option: SetupOption | None = None,
13411340
full_history: bool = False,
13421341
) -> "Codebase":
13431342
"""Fetches a codebase from GitHub and returns a Codebase instance.
@@ -1382,6 +1381,11 @@ def from_repo(
13821381
else:
13831382
# Ensure the operator can handle remote operations
13841383
repo_operator = RepoOperator.create_from_commit(repo_path=repo_path, commit=commit, url=repo_url, full_name=repo_full_name, access_token=access_token)
1384+
1385+
if repo_operator is None:
1386+
logger.error("Failed to clone repository")
1387+
return None
1388+
13851389
logger.info("Clone completed successfully")
13861390

13871391
# Initialize and return codebase with proper context
Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,76 @@
11
import logging
2+
import sys
23

34
import colorlog
45

5-
6-
def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
7-
# Force configure the root logger with a NullHandler to prevent duplicate logs
8-
logging.basicConfig(handlers=[logging.NullHandler()], force=True)
9-
10-
formatter = colorlog.ColoredFormatter(
11-
"%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
12-
log_colors={
6+
formatter = colorlog.ColoredFormatter(
7+
"%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
8+
log_colors={
9+
"DEBUG": "white",
10+
"INFO": "green",
11+
"WARNING": "yellow",
12+
"ERROR": "red",
13+
"CRITICAL": "red,bg_white",
14+
},
15+
secondary_log_colors={
16+
"message": {
1317
"DEBUG": "cyan",
14-
"INFO": "green",
18+
"INFO": "white",
1519
"WARNING": "yellow",
1620
"ERROR": "red",
1721
"CRITICAL": "red,bg_white",
18-
},
19-
secondary_log_colors={
20-
"message": {
21-
"DEBUG": "cyan",
22-
"INFO": "blue",
23-
"WARNING": "yellow",
24-
"ERROR": "red",
25-
"CRITICAL": "red,bg_white",
26-
}
27-
},
28-
)
22+
}
23+
},
24+
)
25+
26+
27+
class StdOutFilter(logging.Filter):
28+
def filter(self, record):
29+
return record.levelno < logging.ERROR
30+
31+
32+
class StdErrFilter(logging.Filter):
33+
def filter(self, record):
34+
return record.levelno >= logging.ERROR
35+
36+
37+
# Create handlers
38+
stdout_handler = logging.StreamHandler(sys.stdout) # Logs to stdout
39+
stdout_handler.setFormatter(formatter)
40+
stdout_handler.addFilter(StdOutFilter())
41+
42+
stderr_handler = logging.StreamHandler(sys.stderr) # Logs to stderr
43+
stderr_handler.setFormatter(formatter)
44+
stderr_handler.addFilter(StdErrFilter())
45+
46+
47+
def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
48+
logger = _setup_logger(name, level)
49+
_setup_exception_logging(logger)
50+
return logger
51+
52+
53+
def _setup_logger(name: str, level: int = logging.INFO) -> logging.Logger:
54+
# Force configure the root logger with a NullHandler to prevent duplicate logs
55+
logging.basicConfig(handlers=[logging.NullHandler()], force=True)
2956
logger = logging.getLogger(name)
3057
if logger.hasHandlers():
3158
for h in logger.handlers:
3259
logger.removeHandler(h)
3360

34-
handler = colorlog.StreamHandler()
35-
handler.setFormatter(formatter)
36-
logger.addHandler(handler)
61+
logger.addHandler(stdout_handler)
62+
logger.addHandler(stderr_handler)
63+
3764
# Ensure the logger propagates to the root logger
3865
logger.propagate = True
3966
# Set the level on the logger itself
4067
logger.setLevel(level)
4168
return logger
69+
70+
71+
def _setup_exception_logging(logger: logging.Logger) -> None:
72+
def log_exception(exc_type, exc_value, exc_traceback):
73+
logger.exception("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
74+
75+
# Set the log_exception function as the exception hook
76+
sys.excepthook = log_exception

tests/integration/codegen/git/codebase/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
import pytest
44

5-
from codegen.git.schemas.enums import SetupOption
65
from codegen.sdk.core.codebase import Codebase
76

87

98
@pytest.fixture
109
def codebase(tmpdir):
1110
os.chdir(tmpdir)
12-
codebase = Codebase.from_repo(repo_full_name="codegen-sh/Kevin-s-Adventure-Game", tmp_dir=tmpdir, language="python", setup_option=SetupOption.PULL_OR_CLONE)
11+
codebase = Codebase.from_repo(repo_full_name="codegen-sh/Kevin-s-Adventure-Game", tmp_dir=tmpdir, language="python")
1312
yield codebase

tests/unit/codegen/sdk/core/test_codebase.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import pytest
44

5+
from codegen.configs.models.secrets import SecretsConfig
56
from codegen.sdk.codebase.codebase_context import CodebaseContext
67
from codegen.sdk.codebase.factory.get_session import get_codebase_session
8+
from codegen.sdk.core.codebase import Codebase
79

810

911
@pytest.fixture(autouse=True)
@@ -39,3 +41,9 @@ def test_codeowners_property(context_mock, codebase):
3941
assert len(codebase.codeowners) == 1
4042
assert callable(codebase.codeowners[0].files_source)
4143
assert codebase.codeowners[0].files_source() == codebase.files.return_value
44+
45+
46+
def test_from_codebase_non_existent_repo(context_mock, tmpdir):
47+
with get_codebase_session(tmpdir=tmpdir, files={"src/main.py": "print('Hello, world!')"}, verify_output=False) as codebase:
48+
codebase = Codebase.from_repo("some-org/non-existent-repo", tmp_dir=tmpdir, secrets=SecretsConfig(github_token="some-token"))
49+
assert codebase is None

0 commit comments

Comments
 (0)