8000 style guide (#49) · moritalous/sdk-python@2d213c1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2d213c1

Browse files
authored
style guide (strands-agents#49)
* style guide * lint: logging
1 parent 6c4a165 commit 2d213c1

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ hatch fmt --linter
9494
9595
If you're using an IDE like VS Code or PyCharm, consider configuring it to use these tools automatically.
9696
97+
For additional details on styling, please see our dedicated [Style Guide](./STYLE_GUIDE.md).
98+
9799
98100
## Contributing via Pull Requests
99101
Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:

STYLE_GUIDE.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Style Guide
2+
3+
## Overview
4+
5+
The Strands Agents style guide aims to establish consistent formatting, naming conventions, and structure across all code in the repository. We strive to make our code clean, readable, and maintainable.
6+
7+
Where possible, we will codify these style guidelines into our linting rules and pre-commit hooks to automate enforcement and reduce the manual review burden.
8+
9+
## Log Formatting
10+
11+
The format for Strands Agents logs is as follows:
12+
13+
```python
14+
logger.debug("field1=<%s>, field2=<%s>, ... | human readable message", field1, field2, ...)
15+
```
16+
17+
### Guidelines
18+
19+
1. **Context**:
20+
- Add context as `<FIELD>=<VALUE>` pairs at the beginning of the log
21+
- Many log services (CloudWatch, Splunk, etc.) look for these patterns to extract fields for searching
22+
- Use `,`'s to separate pairs
23+
- Enclose values in `<>` for readability
24+
- This is particularly helpful in displaying empty values (`field=` vs `field=<>`)
25+
- Use `%s` for string interpolation as recommended by Python logging
26+
- This is an optimization to skip string interpolation when the log level is not enabled
27+
28+
1. **Messages**:
29+
- Add human readable messages at the end of the log
30+
- Use lowercase for consistency
31+
- Avoid punctuation (periods, exclamation points, etc.) to reduce clutter
32+
- Keep messages concise and focused on a single statement
33+
- If multiple statements are needed, separate them with the pipe character (`|`)
34+
- Example: `"processing request | starting validation"`
35+
36+
### Examples
37+
38+
#### Good
39+
40+
```python
41+
logger.debug("user_id=<%s>, action=<%s> | user performed action", user_id, action)
42+
logger.info("request_id=<%s>, duration_ms=<%d> | request completed", request_id, duration)
43+
logger.warning("attempt=<%d>, max_attempts=<%d> | retry limit approaching", attempt, max_attempts)
44+
```
45+
46+
#### Poor
47+
48+
```python
49+
# Avoid: No structured fields, direct variable interpolation in message
50+
logger.debug(f"User {user_id} performed action {action}")
51+
52+
# Avoid: Inconsistent formatting, punctuation
53+
logger.info("Request completed in %d ms.", duration)
54+
55+
# Avoid: No separation between fields and message
56+
logger.warning("Retry limit approaching! attempt=%d max_attempts=%d", attempt, max_attempts)
57+
```
58+
59+
By following these log formatting guidelines, we ensure that logs are both human-readable and machine-parseable, making debugging and monitoring more efficient.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ select = [
185185
"D", # pydocstyle
186186
"E", # pycodestyle
187187
"F", # pyflakes
188+
"G", # logging format
188189
"I", # isort
190+
"LOG", # logging
189191
]
190192

191193
[tool.ruff.lint.per-file-ignores]

src/strands/telemetry/tracer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def __init__(
125125
headers_dict[key.strip()] = value.strip()
126126
otlp_headers = headers_dict
127127
except Exception as e:
128-
logger.warning(f"error=<{e}> | failed to parse OTEL_EXPORTER_OTLP_HEADERS")
128+
logger.warning("error=<%s> | failed to parse OTEL_EXPORTER_OTLP_HEADERS", e)
129129

130130
self.service_name = service_name
131131
self.otlp_endpoint = otlp_endpoint
@@ -184,9 +184,9 @@ def _initialize_tracer(self) -> None:
184184

185185
batch_processor = BatchSpanProcessor(otlp_exporter)
186186
self.tracer_provider.add_span_processor(batch_processor)
187-
logger.info(f"endpoint=<{endpoint}> | OTLP exporter configured with endpoint")
187+
logger.info("endpoint=<%s> | OTLP exporter configured with endpoint", endpoint)
188188
except Exception as e:
189-
logger.error(f"error=<{e}> | Failed to configure OTLP exporter", exc_info=True)
189+
logger.exception("error=<%s> | Failed to configure OTLP exporter", e)
190190

191191
# Set as global tracer provider
192192
trace.set_tracer_provider(self.tracer_provider)
@@ -267,15 +267,15 @@ def _end_span(
267267
else:
268268
span.set_status(StatusCode.OK)
269269
except Exception as e:
270-
logger.warning(f"error=<{e}> | error while ending span", exc_info=True)
270+
logger.warning("error=<%s> | error while ending span", e, exc_info=True)
271271
finally:
272272
span.end()
273273
# Force flush to ensure spans are exported
274274
if self.tracer_provider:
275275
try:
276276
self.tracer_provider.force_flush()
277277
except Exception as e:
278-
logger.warning(f"error=<{e}> | failed to force flush tracer provider")
278+
logger.warning("error=<%s> | failed to force flush tracer provider", e)
279279

280280
def end_span_with_error(self, span: trace.Span, error_message: str, exception: Optional[Exception] = None) -> None:
281281
"""End a span with error status.

0 commit comments

Comments
 (0)
0